diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e7f9bf7..f0611ee1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,57 @@ # TensorRT OSS Release Changelog +## [8.0.1](https://docs.nvidia.com/deeplearning/tensorrt/release-notes/tensorrt-8.html#tensorrt-8) - 2021-07-02 +### Added +- Added support for the following ONNX operators: `Celu`, `CumSum`, `EyeLike`, `GatherElements`, `GlobalLpPool`, `GreaterOrEqual`, `LessOrEqual`, `LpNormalization`, `LpPool`, `ReverseSequence`, and `SoftmaxCrossEntropyLoss` [details](). +- Rehauled `Resize` ONNX operator, now fully supporting the following modes: + - Coordinate Transformation modes: `half_pixel`, `pytorch_half_pixel`, `tf_half_pixel_for_nn`, `asymmetric`, and `align_corners`. + - Modes: `nearest`, `linear`. + - Nearest Modes: `floor`, `ceil`, `round_prefer_floor`, `round_prefer_ceil`. +- Added support for multi-input ONNX `ConvTranpose` operator. +- Added support for 3D spatial dimensions in ONNX `InstanceNormalization`. +- Added support for generic 2D padding in ONNX. +- ONNX `QuantizeLinear` and `DequantizeLinear` operators leverage `IQuantizeLayer` and `IDequantizeLayer`. + - Added support for tensor scales. + - Added support for per-axis quantization. +- Added `EfficientNMS_TRT`, `EfficientNMS_ONNX_TRT` plugins and experimental support for ONNX `NonMaxSuppression` operator. +- Added `ScatterND` plugin. +- Added TensorRT [QuickStart Guide](https://github.com/NVIDIA/TensorRT/tree/master/quickstart). +- Added new samples: [engine_refit_onnx_bidaf](https://docs.nvidia.com/deeplearning/tensorrt/sample-support-guide/index.html#engine_refit_onnx_bidaf) builds an engine from ONNX BiDAF model and refits engine with new weights, [efficientdet](samples/python/efficientdet) and [efficientnet](samples/python/efficientnet) samples for demonstrating Object Detection using TensorRT. +- Added support for Ubuntu20.04 and RedHat/CentOS 8.3. +- Added Python 3.9 support. + +### Changed +- Update Polygraphy to [v0.30.3](tools/Polygraphy/CHANGELOG.md#v0303-2021-06-25). +- Update ONNX-GraphSurgeon to [v0.3.10](tools/onnx-graphsurgeon/CHANGELOG.md#v0310-2021-05-20). +- Update Pytorch Quantization toolkit to v2.1.0. +- Notable TensorRT API updates + - TensorRT now declares API’s with the `noexcept` keyword. All TensorRT classes that an application inherits from (such as IPluginV2) must guarantee that methods called by TensorRT do not throw uncaught exceptions, or the behavior is undefined. + - Destructors for classes with `destroy()` methods were previously protected. They are now public, enabling use of smart pointers for these classes. The `destroy()` methods are deprecated. +- Moved `RefitMap` API from ONNX parser to core TensorRT. +- Various bugfixes for plugins, samples and ONNX parser. +- Port demoBERT to tensorflow2 and update UFF samples to leverage nvidia-tensorflow1 container. + +### Removed +- `IPlugin` and `IPluginFactory` interfaces were deprecated in TensorRT 6.0 and have been removed in TensorRT 8.0. We recommend that you write new plugins or refactor existing ones to target the `IPluginV2DynamicExt` and `IPluginV2IOExt` interfaces. For more information, refer to [Migrating Plugins From TensorRT 6.x Or 7.x To TensorRT 8.x.x](https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#migrating-plugins-6x-7x-to-8x). + - For plugins based on `IPluginV2DynamicExt` and `IPluginV2IOExt`, certain methods with legacy function signatures (derived from `IPluginV2` and `IPluginV2Ext` base classes) which were deprecated and marked for removal in TensorRT 8.0 will no longer be available. +- Removed `samplePlugin` since it showcased IPluginExt interface, which is no longer supported in TensorRT 8.0. +- Removed `sampleMovieLens` and `sampleMovieLensMPS`. +- Removed Dockerfile for Ubuntu 16.04. TensorRT 8.0 debians for Ubuntu 16.04 require python 3.5 while minimum required python version for TensorRT OSS is 3.6. +- Removed support for PowerPC builds, consistent with TensorRT GA releases. + +### Notes +- We had deprecated the Caffe Parser and UFF Parser in TensorRT 7.0. They are still tested and functional in TensorRT 8.0, however, we plan to remove the support in a future release. Ensure you migrate your workflow to use `tf2onnx`, `keras2onnx` or [TensorFlow-TensorRT (TF-TRT)](https://docs.nvidia.com/deeplearning/frameworks/tf-trt-user-guide/index.html). +- Refer to [TensorRT 8.0.1 GA Release Notes](https://docs.nvidia.com/deeplearning/tensorrt/archives/tensorrt-801/release-notes/tensorrt-8.html#rel_8-0-1) for additional details + + ## [21.06](https://github.com/NVIDIA/TensorRT/releases/tag/21.06) - 2021-06-23 +### Added +- Add switch for batch-agnostic mode in NMS plugin +- Add missing model.py in `uff_custom_plugin` sample ### Changed - Update to [Polygraphy v0.29.2](tools/Polygraphy/CHANGELOG.md#v0292-2021-04-30) - Update to [ONNX-GraphSurgeon v0.3.9](tools/onnx-graphsurgeon/CHANGELOG.md#v039-2021-04-20) -- Add missing model.py in `uff_custom_plugin` sample - Fix numerical errors for float type in NMS/batchedNMS plugins - Update demoBERT input dimensions to match Triton requirement [#1051](https://github.com/NVIDIA/TensorRT/pull/1051) - Optimize TLT MaskRCNN plugins: @@ -13,7 +59,6 @@ - Algorithms optimization for NMS kernels and ROIAlign kernel - Fix invalid cuda config issue when bs is larger than 32 - Fix issues found on Jetson NANO -- Add switch for batch-agnostic mode in NMS plugin ### Removed - Removed fcplugin from demoBERT to improve latency diff --git a/CMakeLists.txt b/CMakeLists.txt index a18cbd01..ba3af18f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,8 +80,8 @@ endif() ############################################################################################ # Dependencies -set(DEFAULT_CUDA_VERSION 11.1) -set(DEFAULT_CUDNN_VERSION 8.0) +set(DEFAULT_CUDA_VERSION 11.3.1) +set(DEFAULT_CUDNN_VERSION 8.2) set(DEFAULT_PROTOBUF_VERSION 3.0.0) set(DEFAULT_CUB_VERSION 1.8.0) diff --git a/README.md b/README.md index d30b525d..6bb3bb28 100644 --- a/README.md +++ b/README.md @@ -15,16 +15,16 @@ This repository contains the Open Source Software (OSS) components of NVIDIA Ten To build the TensorRT-OSS components, you will first need the following software packages. **TensorRT GA build** -* [TensorRT](https://developer.nvidia.com/nvidia-tensorrt-download) v7.2.3.4 +* [TensorRT](https://developer.nvidia.com/nvidia-tensorrt-download) v8.0.1.6 **System Packages** * [CUDA](https://developer.nvidia.com/cuda-toolkit) * Recommended versions: - * cuda-11.x + cuDNN-8.1 - * cuda-10.2 + cuDNN-8.1 + * cuda-11.3.1 + cuDNN-8.2 + * cuda-10.2 + cuDNN-8.2 * [GNU make](https://ftp.gnu.org/gnu/make/) >= v4.1 * [cmake](https://github.com/Kitware/CMake/releases) >= v3.13 -* [python]() >= v3.6.5 +* [python]() >= v3.6.9 * [pip](https://pypi.org/project/pip/#history) >= v19.0 * Essential utilities * [git](https://git-scm.com/downloads), [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/), [wget](https://www.gnu.org/software/wget/faq.html#download), [zlib](https://zlib.net/) @@ -34,16 +34,16 @@ To build the TensorRT-OSS components, you will first need the following software * [Docker](https://docs.docker.com/install/) >= 19.03 * [NVIDIA Container Toolkit](https://github.com/NVIDIA/nvidia-docker) * Toolchains and SDKs - * (Cross compilation for Jetson platform) [NVIDIA JetPack](https://developer.nvidia.com/embedded/jetpack) >= 4.4 + * (Cross compilation for Jetson platform) [NVIDIA JetPack](https://developer.nvidia.com/embedded/jetpack) >= 4.6 (July 2021) * (For Windows builds) [Visual Studio](https://visualstudio.microsoft.com/vs/older-downloads/) 2017 Community or Enterprise edition * (Cross compilation for QNX platform) [QNX Toolchain](https://blackberry.qnx.com/en) * PyPI packages (for demo applications/tests) - * [onnx](https://pypi.org/project/onnx/1.7.0/) 1.7.0 - * [onnxruntime](https://pypi.org/project/onnxruntime/1.6.0/) >= 1.6.0 - * [tensorflow-gpu](https://pypi.org/project/tensorflow/2.2.2/) >= 2.2.2 - * [Pillow](https://pypi.org/project/Pillow/8.1.2/) >= 8.1.2 - * [pycuda](https://pypi.org/project/pycuda/) - * [numpy](https://pypi.org/project/numpy/) + * [onnx](https://pypi.org/project/onnx/) 1.8.0 + * [onnxruntime](https://pypi.org/project/onnxruntime/) 1.7.0 + * [tensorflow-gpu](https://pypi.org/project/tensorflow/) >= 2.4.1 + * [Pillow](https://pypi.org/project/Pillow/) >= 8.1.2 + * [pycuda](https://pypi.org/project/pycuda/) < 2020.1 + * [numpy](https://pypi.org/project/numpy/) 1.21.0 * [pytest](https://pypi.org/project/pytest/) * Code formatting tools (for contributors) * [Clang-format](https://clang.llvm.org/docs/ClangFormat.html) @@ -54,46 +54,38 @@ To build the TensorRT-OSS components, you will first need the following software ## Downloading TensorRT Build 1. #### Download TensorRT OSS - **On Linux: Bash** ```bash git clone -b master https://github.com/nvidia/TensorRT TensorRT cd TensorRT git submodule update --init --recursive ``` - **On Windows: Powershell** - ```powershell - git clone -b master https://github.com/nvidia/TensorRT TensorRT - cd TensorRT - git submodule update --init --recursive - ``` -2. #### Specify the TensorRT Release build +2. #### (Optional - if not using TensorRT container) Specify the TensorRT GA release build - If using NVIDIA build containers, TensorRT is preinstalled under `/usr/lib/x86_64-linux-gnu`. + If using the TensorRT OSS build container, TensorRT libraries are preinstalled under `/usr/lib/x86_64-linux-gnu` and you may skip this step. - Else download and extract the TensorRT build from [NVIDIA Developer Zone](https://developer.nvidia.com/nvidia-tensorrt-download). + Else download and extract the TensorRT GA build from [NVIDIA Developer Zone](https://developer.nvidia.com/nvidia-tensorrt-download). - **Example: Ubuntu 18.04 on x86-64 with cuda-11.1** + **Example: Ubuntu 18.04 on x86-64 with cuda-11.3** ```bash cd ~/Downloads - tar -xvzf TensorRT-7.2.3.4.Ubuntu-18.04.x86_64-gnu.cuda-11.1.cudnn8.1.tar.gz - export TRT_LIBPATH=`pwd`/TensorRT-7.2.3.4 + tar -xvzf TensorRT-8.0.1.6.Ubuntu-18.04.x86_64-gnu.cuda-11.3.cudnn8.2.tar.gz + export TRT_LIBPATH=`pwd`/TensorRT-8.0.1.6 ``` - **Example: Windows on x86-64 with cuda-11.0** + **Example: Windows on x86-64 with cuda-11.3** ```powershell cd ~\Downloads - Expand-Archive .\TensorRT-7.2.3.4.Windows10.x86_64.cuda-11.0.cudnn8.1.zip - $Env:TRT_LIBPATH = '$(Get-Location)\TensorRT-7.2.3.4' + Expand-Archive .\TensorRT-8.0.1.6.Windows10.x86_64.cuda-11.3.cudnn8.2.zip + $Env:TRT_LIBPATH = '$(Get-Location)\TensorRT-8.0.1.6' $Env:PATH += 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\' ``` -3. #### (Optional) JetPack SDK for Jetson builds - Using the JetPack SDK manager, download the host components. Steps: - 1. Download and launch the SDK manager. Login with your developer account. +3. #### (Optional - for Jetson builds only) Download the JetPack SDK + 1. Download and launch the JetPack SDK manager. Login with your NVIDIA developer account. 2. Select the platform and target OS (example: Jetson AGX Xavier, `Linux Jetpack 4.4`), and click Continue. 3. Under `Download & Install Options` change the download folder and select `Download now, Install later`. Agree to the license terms and click Continue. 4. Move the extracted files into the `/docker/jetpack_files` folder. @@ -101,42 +93,39 @@ To build the TensorRT-OSS components, you will first need the following software ## Setting Up The Build Environment -For native builds, install the [prerequisite](#prerequisites) *System Packages*. Alternatively (recommended for non-Windows builds), install Docker and generate a build container as described below: +For Linux platforms, we recommend that you generate a docker container for building TensorRT OSS as described below. For native builds, on Windows for example, please install the [prerequisite](#prerequisites) *System Packages*. 1. #### Generate the TensorRT-OSS build container. - The TensorRT-OSS build container can be generated using the Dockerfiles and build script included with TensorRT-OSS. The build container is bundled with packages and environment required for building TensorRT OSS. + The TensorRT-OSS build container can be generated using the supplied Dockerfiles and build script. The build container is configured for building TensorRT OSS out-of-the-box. - **Example: Ubuntu 18.04 on x86-64 with cuda-11.1** + **Example: Ubuntu 18.04 on x86-64 with cuda-11.3** ```bash - ./docker/build.sh --file docker/ubuntu-18.04.Dockerfile --tag tensorrt-ubuntu-1804 --cuda 11.1 + ./docker/build.sh --file docker/ubuntu-18.04.Dockerfile --tag tensorrt-ubuntu18.04-cuda11.3 --cuda 11.3.1 ``` - **Example: Ubuntu 18.04 cross-compile for PowerPC with cuda-11.0** + **Example: CentOS/RedHat 8 on x86-64 with cuda-10.2** ```bash - ./docker/build.sh --file docker/ubuntu-cross-ppc64le.Dockerfile --tag tensorrt-ubuntu-ppc --cuda 11.0 + ./docker/build.sh --file docker/centos-8.Dockerfile --tag tensorrt-centos8-cuda10.2 --cuda 10.2 ``` - **Example: CentOS/RedHat 7 on x86-64 with cuda-11.0** + **Example: Ubuntu 18.04 cross-compile for Jetson (aarch64) with cuda-10.2 (JetPack SDK)** ```bash - ./docker/build.sh --file docker/centos-7.Dockerfile --tag tensorrt-centos --cuda 11.0 - ``` - **Example: Ubuntu 18.04 cross-compile for Jetson (arm64) with cuda-10.2 (JetPack SDK)** - ```bash - ./docker/build.sh --file docker/ubuntu-cross-aarch64.Dockerfile --tag tensorrt-cross-jetpack --cuda 10.2 + ./docker/build.sh --file docker/ubuntu-cross-aarch64.Dockerfile --tag tensorrt-jetpack-cuda10.2 --cuda 10.2 ``` 2. #### Launch the TensorRT-OSS build container. **Example: Ubuntu 18.04 build container** ```bash - ./docker/launch.sh --tag tensorrt-ubuntu-1804 --gpus all + ./docker/launch.sh --tag tensorrt-ubuntu18.04-cuda11.3 --gpus all ``` > NOTE: - 1. Use the tag corresponding to the build container. - 2. To run TensorRT/CUDA programs in the build container, install [NVIDIA Container Toolkit](#prerequisites). Docker versions < 19.03 require `nvidia-docker2` and `--runtime=nvidia` flag for docker run commands. On versions >= 19.03, you need the `nvidia-container-toolkit` package and `--gpus all` flag. - 3. sudo password for Ubuntu containers is 'nvidia'. + 1. Use the `--tag` corresponding to build container generated in Step 1. + 2. [NVIDIA Container Toolkit](#prerequisites) is required for GPU access (running TensorRT applications) inside the build container. + 3. `sudo` password for Ubuntu build containers is 'nvidia'. + 4. Specify port number using `--jupyter ` for launching Jupyter notebooks. ## Building TensorRT-OSS * Generate Makefiles or VS project (Windows) and build. - **Example: Linux (x86-64) build with default cuda-11.1** + **Example: Linux (x86-64) build with default cuda-11.3** ```bash cd $TRT_OSSPATH mkdir -p build && cd build @@ -157,13 +146,6 @@ For native builds, install the [prerequisite](#prerequisites) *System Packages*. cmake .. -DTRT_LIB_DIR=$TRT_LIBPATH -DTRT_OUT_DIR=`pwd`/out -DCMAKE_TOOLCHAIN_FILE=$TRT_OSSPATH/cmake/toolchains/cmake_aarch64.toolchain -DCUDA_VERSION=10.2 make -j$(nproc) ``` - **Example: Cross-Compile for QNX with cuda-10.2** - ```bash - cd $TRT_OSSPATH - mkdir -p build && cd build - cmake .. -DTRT_LIB_DIR=$TRT_LIBPATH -DTRT_OUT_DIR=`pwd`/out -DCMAKE_TOOLCHAIN_FILE=$TRT_OSSPATH/cmake/toolchains/cmake_qnx.toolchain -DCUDA_VERSION=10.2 - make -j$(nproc) - ``` **Example: Windows (x86-64) build in Powershell** ```powershell cd $Env:TRT_OSSPATH @@ -172,16 +154,15 @@ For native builds, install the [prerequisite](#prerequisites) *System Packages*. msbuild ALL_BUILD.vcxproj ``` > NOTE: - 1. The default CUDA version used by CMake is 11.1. To override this, for example to 10.2, append `-DCUDA_VERSION=10.2` to the cmake command. - 2. If samples fail to link on CentOS7, create this symbolic link: `ln -s $TRT_OUT_DIR/libnvinfer_plugin.so $TRT_OUT_DIR/libnvinfer_plugin.so.7` + 1. The default CUDA version used by CMake is 11.3.1. To override this, for example to 10.2, append `-DCUDA_VERSION=10.2` to the cmake command. + 2. If samples fail to link on CentOS7, create this symbolic link: `ln -s $TRT_OUT_DIR/libnvinfer_plugin.so $TRT_OUT_DIR/libnvinfer_plugin.so.8` * Required CMake build arguments are: - `TRT_LIB_DIR`: Path to the TensorRT installation directory containing libraries. - `TRT_OUT_DIR`: Output directory where generated build artifacts will be copied. * Optional CMake build arguments: - `CMAKE_BUILD_TYPE`: Specify if binaries generated are for release or debug (contain debug symbols). Values consists of [`Release`] | `Debug` - - `CUDA_VERISON`: The version of CUDA to target, for example [`11.1`]. - - `CUDNN_VERSION`: The version of cuDNN to target, for example [`8.0`]. - - `NVCR_SUFFIX`: Optional nvcr/cuda image suffix. Set to "-rc" for CUDA11 RC builds until general availability. Blank by default. + - `CUDA_VERISON`: The version of CUDA to target, for example [`11.3.1`]. + - `CUDNN_VERSION`: The version of cuDNN to target, for example [`8.2`]. - `PROTOBUF_VERSION`: The version of Protobuf to use, for example [`3.0.0`]. Note: Changing this will not configure CMake to use a system version of Protobuf, it will configure CMake to download and try building that version. - `CMAKE_TOOLCHAIN_FILE`: The path to a toolchain file for cross compilation. - `BUILD_PARSERS`: Specify if the parsers should be built, for example [`ON`] | `OFF`. If turned OFF, CMake will try to find precompiled versions of the parser libraries to use in compiling samples. First in `${TRT_LIB_DIR}`, then on the system. If the build type is Debug, then it will prefer debug builds of the libraries before release versions if available. @@ -199,13 +180,14 @@ For native builds, install the [prerequisite](#prerequisites) *System Packages*. ## TensorRT Resources -* [TensorRT Homepage](https://developer.nvidia.com/tensorrt) +* [TensorRT Developer Home](https://developer.nvidia.com/tensorrt) +* [TensorRT QuickStart Guide](https://docs.nvidia.com/deeplearning/tensorrt/quick-start-guide/index.html) * [TensorRT Developer Guide](https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html) * [TensorRT Sample Support Guide](https://docs.nvidia.com/deeplearning/tensorrt/sample-support-guide/index.html) +* [TensorRT ONNX Tools](https://docs.nvidia.com/deeplearning/tensorrt/index.html#tools) * [TensorRT Discussion Forums](https://devtalk.nvidia.com/default/board/304/tensorrt/) -* [TensorRT Release Notes](https://docs.nvidia.com/deeplearning/tensorrt/release-notes/index.html). +* [TensorRT Release Notes](https://docs.nvidia.com/deeplearning/tensorrt/release-notes/index.html) ## Known Issues -#### TensorRT 7.2.3.4 * None diff --git a/VERSION b/VERSION index f9980270..fd49265d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.2.3.4 +8.0.1.6 diff --git a/cmake/toolchains/cmake_aarch64.toolchain b/cmake/toolchains/cmake_aarch64.toolchain index 7fbde2ff..2bb8ffa1 100644 --- a/cmake/toolchains/cmake_aarch64.toolchain +++ b/cmake/toolchains/cmake_aarch64.toolchain @@ -32,6 +32,8 @@ set(CMAKE_C_COMPILER_FORCED TRUE) set(CMAKE_CXX_COMPILER_FORCED TRUE) set(CUDA_ROOT /usr/local/cuda-${CUDA_VERSION}/targets/${CUDA_PLATFORM_ID} CACHE STRING "CUDA ROOT dir") +set(CUDNN_ROOT_DIR /pdk_files/cudnn) +set(BUILD_LIBRARY_ONLY 1) set(CUDA_TOOLKIT_ROOT_DIR ${CUDA_ROOT}) set(CUDA_INCLUDE_DIRS ${CUDA_ROOT}/include) @@ -39,7 +41,7 @@ set(CUDA_INCLUDE_DIRS ${CUDA_ROOT}/include) set(RT_LIB /usr/aarch64-linux-gnu/lib/librt.so) set(CMAKE_CUDA_HOST_COMPILER ${CMAKE_CXX_COMPILER} CACHE STRING "" FORCE) -set(CMAKE_CUDA_FLAGS "-cudart none -I${CUDA_INCLUDE_DIRS} -Xcompiler=\"-fPIC ${CMAKE_CXX_FLAGS}\"" CACHE STRING "" FORCE) +set(CMAKE_CUDA_FLAGS "-I${CUDA_INCLUDE_DIRS} -Xcompiler=\"-fPIC ${CMAKE_CXX_FLAGS}\"" CACHE STRING "" FORCE) set(CMAKE_CUDA_COMPILER_FORCED TRUE) set(CUDA_LIBS -L${CUDA_ROOT}/lib) diff --git a/demo/BERT/README.md b/demo/BERT/README.md old mode 100644 new mode 100755 index 07d5d49f..7e65eb09 --- a/demo/BERT/README.md +++ b/demo/BERT/README.md @@ -20,6 +20,11 @@ This subfolder of the BERT TensorFlow repository, tested and maintained by NVIDI - [Accuracy](#accuracy) * [Evaluating Post-Training-Quantization INT8 accuracy](#evaluating-ptq-post-training-quantization-int8-accuracy-using-the-squad-dataset) * [Evaluating Quantization-Aware-Training INT8 accuracy](#evaluating-qat-quantization-aware-training-int8-accuracy-using-the-squad-dataset) +- [Experimental](#experimental) + * [Variable sequence length](#variable-sequence-length) + * [Run command lines](#run-command-lines) + * [Sparsity with Quantization Aware Training](#sparsity-with-quantization-aware-training) + * [Megatron-LM for Question Answering](#megatron-lm-for-question-answering) - [Performance](#performance) * [Benchmarking](#benchmarking) * [TensorRT inference benchmark](#tensorrt-inference-benchmark) @@ -33,9 +38,6 @@ This subfolder of the BERT TensorFlow repository, tested and maintained by NVIDI * [Inference performance: NVIDIA V100](#inference-performance-nvidia-v100-16gb) * [BERT Base](#bert-base-2) * [BERT Large](#bert-large-2) -- [Experimental](#experimental) - * [Variable sequence length](#variable-sequence-length) - * [Run command lines](#run-command-lines) ## Model overview @@ -78,9 +80,9 @@ The following software version configuration has been tested: |Software|Version| |--------|-------| -|Python|3.6.9| -|TensorRT|7.2.3.4| -|CUDA|11.1.1| +|Python|>=3.6.x| +|TensorRT|8.0.1.6| +|CUDA|11.3.1| ## Setup @@ -93,9 +95,10 @@ This demo BERT application can be run within the TensorRT OSS build container. I * [NGC CLI](https://ngc.nvidia.com/setup/installers/cli) - for downloading BERT checkpoints from NGC. * PyPI Packages: - * [pycuda](https://pypi.org/project/pycuda/) (tested 2019.1.2) - * [onnx](https://pypi.org/project/onnx/1.7.0/) (tested 1.7.0) - * [tensorflow](https://pypi.org/project/tensorflow/) >= 2.2 + * [pycuda](https://pypi.org/project/pycuda/) (tested v2019.1.2) + * [onnx](https://pypi.org/project/onnx) (tested v1.8.1) + * [tensorflow](https://pypi.org/project/tensorflow/) (tested v2.4.1) + * [torch](https://pypi.org/project/torch/1.6.0/) (tested v1.8.1) * NVIDIA [Volta](https://www.nvidia.com/en-us/data-center/volta-gpu-architecture/), [Turing](https://www.nvidia.com/en-us/geforce/turing/) or [Ampere](https://www.nvidia.com/en-us/data-center/nvidia-ampere-gpu-architecture/) based GPU. @@ -133,7 +136,7 @@ This demo BERT application can be run within the TensorRT OSS build container. I mkdir -p engines && python3 builder.py -m models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_128_v19.03.1/model.ckpt -o engines/bert_large_128.engine -b 1 -s 128 --fp16 -c models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_128_v19.03.1 ``` - This will build an engine with a maximum batch size of 1 (`-b 1`), and sequence length of 128 (`-s 128`) using mixed precision (`--fp16`) using the BERT Large SQuAD v2 FP16 Sequence Length 128 checkpoint (`-c /workspace/TensorRT/demo/BERT/models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_128_v19.03.1`). + This will build an engine with a maximum batch size of 1 (`-b 1`), and sequence length of 128 (`-s 128`) using mixed precision (`--fp16`) using the BERT Large SQuAD v2 FP16 Sequence Length 128 checkpoint (`-c models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_128_v19.03.1`). 5. Run inference. Two options are provided for running the model. @@ -161,7 +164,7 @@ This demo BERT application can be run within the TensorRT OSS build container. I cmake .. -DPYTHON_EXECUTABLE=$(which python) make -j popd - python3 inference_c.py -e /workspace/TensorRT/demo/BERT/engines/bert_large_128.engine --enable-graph -p "TensorRT is a high performance deep learning inference platform that delivers low latency and high throughput for apps such as recommenders, speech and image/video on NVIDIA GPUs. It includes parsers to import models, and plugins to support novel ops and layers before applying optimizations for inference. Today NVIDIA is open-sourcing parsers and plugins in TensorRT so that the deep learning community can customize and extend these components to take advantage of powerful TensorRT optimizations for your apps." -q "What is TensorRT?" -v /workspace/TensorRT/demo/BERT/models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_128_v19.03.1/vocab.txt + python3 inference_c.py -e engines/bert_large_128.engine --enable-graph -p "TensorRT is a high performance deep learning inference platform that delivers low latency and high throughput for apps such as recommenders, speech and image/video on NVIDIA GPUs. It includes parsers to import models, and plugins to support novel ops and layers before applying optimizations for inference. Today NVIDIA is open-sourcing parsers and plugins in TensorRT so that the deep learning community can customize and extend these components to take advantage of powerful TensorRT optimizations for your apps." -q "What is TensorRT?" -v models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_128_v19.03.1/vocab.txt ``` A separate C/C++ inference benchmark executable `perf` (compiled from `perf.cpp`) is provided to run inference benchmarks with CUDA Graph. The cmdline interface is the same as `perf.py` except for an extra `--enable_graph` option. @@ -293,6 +296,106 @@ As mentioned in the [Quick Start Guide](#quick-start-guide), two options are pro python3 inference.py -e engines/bert_large_384_int8mix.engine -s 384 -sq ./squad/dev-v1.1.json -v models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_384_v19.03.1/vocab.txt -o ./predictions.json python3 squad/evaluate-v1.1.py squad/dev-v1.1.json ./predictions.json 90 ``` +## Experimental + +### Variable sequence length +In our prior implementation, we used inputs padded to max length along with corresponding input masks to handle variable sequence length inputs in a batch. The padding results in some wasted computations which can be avoided by handling variable sequence length inputs natively. Now we have a new approach called the variable sequence length method. By concatenating each input id into a single long input id, and concatenating each input segment id into a single long segment id, TensorRT can know the exact starts and ends by providing an extra sequence length buffer that contains the start and end positions of each sequence. Now we can eliminate the wasted computation in the input paddings. + +Note this is an experimental feature because we only support Xavier+ GPUs, also there is neither FP32 support nor INT8 PTQ calibration. + +1. Download checkpoint for BERT Large FP16 SQuAD v1.1 model with sequence length of 384: + ```bash + bash scripts/download_model.sh pyt v1_1 + ``` + +2. Build an engine: + + **FP16 engine** + ```bash + mkdir -p engines && python3 builder_varseqlen.py -x models/fine-tuned/bert_pyt_onnx_large_qa_squad11_amp_fake_quant_v1/bert_large_v1_1_fake_quant.onnx -o engines/bert_varseq_fp16.engine -b 1 -s 64 --fp16 -c models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_384_v19.03.1 -v models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_384_v19.03.1/vocab.txt + ``` + + This will build and engine with a maximum batch size of 1 (`-b 1`) and sequence length of 64 (`-s 64`) using FP16 precision computation where possible (`--fp16`). + + + **INT8 engine** + ```bash + mkdir -p engines && python3 builder_varseqlen.py -x models/fine-tuned/bert_pyt_onnx_large_qa_squad11_amp_fake_quant_v1/bert_large_v1_1_fake_quant.onnx -o engines/bert_varseq_int8.engine -b 1 -s 256 --int8 --fp16 -c models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_384_v19.03.1 -v models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_384_v19.03.1/vocab.txt + ``` + + This will build and engine with a maximum batch size of 1 (`-b 1`) and sequence length of 256 (`-s 256`) using INT8 precision computation where possible (`--int8`). + +3. Run inference + + Evaluate the F1 score and exact match score using the squad dataset: + + ```bash + python3 inference_varseqlen.py -e engines/bert_varseq_int8.engine -s 256 -sq ./squad/dev-v1.1.json -v models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_384_v19.03.1/vocab.txt -o ./predictions.json + python3 squad/evaluate-v1.1.py squad/dev-v1.1.json ./predictions.json 90 + ``` + + Run the quesion and answer mode: + + ```bash + python3 inference_varseqlen.py -e engines/bert_varseq_int8.engine -p "TensorRT is a high performance deep learning inference platform that delivers low latency and high throughput for apps such as recommenders, speech and image/video on NVIDIA GPUs. It includes parsers to import models, and plugins to support novel ops and layers before applying optimizations for inference. Today NVIDIA is open-sourcing parsers and plugins in TensorRT so that the deep learning community can customize and extend these components to take advantage of powerful TensorRT optimizations for your apps." -q "What is TensorRT?" -v models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_384_v19.03.1/vocab.txt -s 256 + ``` + +4. Collect performance data + + ```bash + python3 perf_varseqlen.py -e engines/bert_varseq_int8.engine -b 1 -s 256 + ``` + + This will collect performance data run use batch size 1 (`-b 1`) and sequence length of 256 (`-s 256`). + +5. Collect performance data with CUDA graph enabled + + We can use the same `inference_c.py` and `build/perf` to collect performance data with cuda graph enabled. The command line is the same as run without variable sequence length. + +### Sparsity with Quantization Aware Training + +Fine-grained 2:4 structured sparsity support introduced in NVIDIA Ampere GPUs can produce significant performance gains in BERT inference. The network is first trained using dense weights, then fine-grained structured pruning is applied, and finally the remaining non-zero weights are fine-tuned with additional training steps. This method results in virtually no loss in inferencing accuracy. + +Using INT8 precision with quantization scales obtained from Post-Training Quantization (PTQ) can produce additional performance gains, but may also result in accuracy loss. Alternatively, for PyTorch-trained models, NVIDIA [PyTorch-Quantization toolkit](https://github.com/NVIDIA/TensorRT/tree/master/tools/pytorch-quantization) can be leveraged to perform quantized fine tuning (a.k.a. Quantization Aware Training or QAT) and generate the INT8 quantization scales as part of training. This generally results in higher accuracy compared to PTQ. + +To demonstrate the potential speedups from these optimizations in demoBERT, we provide the [Megatron-LM](https://github.com/NVIDIA/Megatron-LM) transformer model finetuned for SQuAD 2.0 task with sparsity and quantization. + +The sparse weights are generated by finetuning with INT8 Quantization Aware Training recipe. This feature can be used with the fixed or variable sequence length implementations by passing in `-sp` flag to demoBERT builder. + +#### Megatron-LM for Question Answering + +##### Example: Megatron-LM Large SQuAD v2.0 with sparse weights for sequence length 384 + +**Build the TensorRT engine**: + +Options specified: +* `--megatron` : assume Megatron style residuals instead of vanilla BERT. +* `--pickle` : specify a pickle file containing the PyTorch statedict corresponding to fine-tuned Megatron model. +* `-sp` : enable sparsity during engine optimization and treat the weights as sparse. +* `--int8 --il` : enable int8 tactics/plugins with interleaving. + +```bash +bash ./scripts/download_model.sh 384 v1_1 # BERT-large model checkpoint fine-tuned for SQuAD 1.1 +bash ./scripts/download_model.sh pyt megatron-large int8-qat sparse # Megatron-LM model weights +export CKPT_PATH=models/fine-tuned/bert_pyt_statedict_megatron_sparse_int8qat_v21.03.0/bert_pyt_statedict_megatron_sparse_int8_qat +mkdir -p engines && python3 builder_varseqlen.py -c models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_384_v19.03.1 -b 1 -s 384 -o engines/megatron_large_seqlen384_int8qat_sparse.engine --fp16 --int8 --strict -il --megatron --pickle $CKPT_PATH -v models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_384_v19.03.1/vocab.txt -sp +``` + +**Ask a question**: +```bash +python3 inference_varseqlen.py -e engines/megatron_large_seqlen384_int8qat_sparse.engine -p "TensorRT is a high performance deep learning inference platform that delivers low latency and high throughput for apps such as recommenders, speech and image/video on NVIDIA GPUs. It includes parsers to import models, and plugins to support novel ops and layers before applying optimizations for inference. Today NVIDIA is open-sourcing parsers and plugins in TensorRT so that the deep learning community can customize and extend these components to take advantage of powerful TensorRT optimizations for your apps." -q "What is TensorRT?" -v models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_384_v19.03.1/vocab.txt -s 256 +``` + +**Evaluate F1 score**: +```bash +python3 inference_varseqlen.py -e engines/megatron_large_seqlen384_int8qat_sparse.engine -s 384 -sq ./squad/dev-v1.1.json -v models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_384_v19.03.1/vocab.txt -o ./predictions.json +python3 squad/evaluate-v1.1.py squad/dev-v1.1.json ./predictions.json 90 +``` +Expected output: +``` +&&&& PASSED TensorRT BERT Squad Accuracy matches reference. +{"exact_match": 84.03973509933775, "f1": 90.88667129897755} +``` ## Performance @@ -322,231 +425,4 @@ Also note that BERT Large engines, especially using mixed precision with large b ### Results -The following sections provide details on how we achieved our performance and inference. - -#### Inference performance: NVIDIA A100 (40GB) - -Our results were obtained by running the `scripts/inference_benchmark.sh --gpu Ampere` script in the container generated by the TensorRT OSS Dockerfile on NVIDIA A100 with (1x A100 40G) GPUs. - -##### BERT Base - -| Sequence Length | Batch Size | INT8 Latency (ms) | | | FP16 Latency (ms) | | | -|-----------------|------------|-----------------|-----------------|---------|-----------------|-----------------|---------| -| | | 95th Percentile | 99th Percentile | Average | 95th Percentile | 99th Percentile | Average | -| 128 | 1 | 0.77 | 0.77 | 0.77 | 0.78 | 0.80 | 0.78 | -| 128 | 2 | 0.76 | 0.77 | 0.76 | 0.92 | 0.93 | 0.92 | -| 128 | 4 | 0.93 | 1.18 | 0.93 | 1.19 | 1.51 | 1.19 | -| 128 | 8 | 1.19 | 1.20 | 1.19 | 1.78 | 1.78 | 1.77 | -| 128 | 12 | 1.57 | 1.57 | 1.56 | 2.07 | 2.08 | 2.05 | -| 128 | 16 | 1.88 | 1.89 | 1.88 | 2.54 | 2.60 | 2.52 | -| 128 | 24 | 2.65 | 2.65 | 2.64 | 3.65 | 3.70 | 3.61 | -| 128 | 32 | 3.21 | 3.22 | 3.21 | 4.71 | 4.74 | 4.67 | -| 128 | 64 | 5.69 | 5.70 | 5.64 | 8.87 | 8.96 | 8.81 | -| 128 | 128 | 10.84 | 10.85 | 10.70 | 17.61 | 17.62 | 17.44 | -| 384 | 1 | 1.34 | 1.35 | 1.34 | 1.46 | 1.46 | 1.45 | -| 384 | 2 | 1.56 | 1.79 | 1.56 | 1.85 | 1.85 | 1.84 | -| 384 | 4 | 2.02 | 2.03 | 2.02 | 2.46 | 2.46 | 2.45 | -| 384 | 8 | 2.94 | 2.95 | 2.94 | 3.91 | 3.92 | 3.86 | -| 384 | 12 | 4.07 | 4.07 | 4.06 | 5.54 | 5.55 | 5.47 | -| 384 | 16 | 5.22 | 5.23 | 5.21 | 7.78 | 7.79 | 7.69 | -| 384 | 24 | 7.42 | 7.42 | 7.37 | 10.75 | 10.76 | 10.63 | -| 384 | 32 | 9.92 | 9.93 | 9.77 | 14.58 | 14.73 | 14.52 | -| 384 | 64 | 18.74 | 18.78 | 18.61 | 28.66 | 28.70 | 28.39 | -| 384 | 128 | 36.40 | 36.42 | 36.05 | 55.36 | 55.90 | 55.21 | - -##### BERT Large - -| Sequence Length | Batch Size | INT8 Latency (ms) | | | FP16 Latency (ms) | | | -|-----------------|------------|-----------------|-----------------|---------|-----------------|-----------------|---------| -| | | 95th Percentile | 99th Percentile | Average | 95th Percentile | 99th Percentile | Average | -| 128 | 1 | 1.60 | 1.61 | 1.60 | 1.87 | 1.88 | 1.87 | -| 128 | 2 | 1.94 | 1.95 | 1.94 | 2.36 | 2.37 | 2.35 | -| 128 | 4 | 2.45 | 2.46 | 2.44 | 3.36 | 3.37 | 3.36 | -| 128 | 8 | 3.82 | 3.83 | 3.79 | 4.98 | 5.00 | 4.95 | -| 128 | 12 | 4.22 | 4.23 | 4.22 | 6.45 | 6.46 | 6.38 | -| 128 | 16 | 5.75 | 5.75 | 5.74 | 8.50 | 8.53 | 8.43 | -| 128 | 24 | 7.10 | 7.11 | 7.04 | 11.47 | 11.49 | 11.31 | -| 128 | 32 | 9.61 | 9.61 | 9.51 | 15.49 | 15.50 | 15.25 | -| 128 | 64 | 17.25 | 17.25 | 17.11 | 29.43 | 29.73 | 29.29 | -| 128 | 128 | 33.25 | 33.58 | 33.05 | 56.98 | 57.17 | 56.68 | -| 384 | 1 | 3.00 | 3.01 | 2.99 | 3.52 | 3.53 | 3.51 | -| 384 | 2 | 3.71 | 3.72 | 3.71 | 4.97 | 4.99 | 4.97 | -| 384 | 4 | 5.08 | 5.09 | 5.08 | 7.01 | 7.01 | 6.92 | -| 384 | 8 | 9.04 | 9.05 | 9.04 | 12.71 | 12.72 | 12.67 | -| 384 | 12 | 11.65 | 11.71 | 11.57 | 18.24 | 18.25 | 18.04 | -| 384 | 16 | 15.63 | 15.63 | 15.49 | 24.24 | 24.28 | 23.94 | -| 384 | 24 | 22.57 | 22.61 | 22.36 | 35.77 | 35.78 | 35.38 | -| 384 | 32 | 29.66 | 29.66 | 29.33 | 47.09 | 47.11 | 46.81 | -| 384 | 64 | 57.20 | 57.34 | 56.93 | 92.12 | 92.49 | 91.61 | -| 384 | 128 | 112.00 | 112.42 | 111.24 | 180.61 | 181.02 | 179.56 | - - -#### Inference performance: NVIDIA T4 (16GB) - -Our results were obtained by running the `scripts/inference_benchmark.sh --gpu Turing` script in the container generated by the TensorRT OSS Dockerfile on NVIDIA T4 with (1x T4 16G) GPUs. - -##### BERT Base - -| Sequence Length | Batch Size | INT8 Latency (ms) | | | FP16 Latency (ms) | | | -|-----------------|------------|-----------------|-----------------|---------|-----------------|-----------------|---------| -| | | 95th Percentile | 99th Percentile | Average | 95th Percentile | 99th Percentile | Average | -| 128 | 1 | 1.67 | 1.67 | 1.66 | 1.82 | 1.96 | 1.76 | -| 128 | 2 | 1.94 | 1.95 | 1.89 | 2.58 | 2.67 | 2.50 | -| 128 | 4 | 2.73 | 2.80 | 2.64 | 4.30 | 4.34 | 4.17 | -| 128 | 8 | 4.93 | 4.96 | 4.81 | 8.85 | 9.74 | 8.36 | -| 128 | 12 | 6.85 | 7.05 | 6.70 | 12.83 | 13.19 | 12.34 | -| 128 | 16 | 9.65 | 9.89 | 9.43 | 17.70 | 18.27 | 17.01 | -| 128 | 24 | 15.04 | 15.70 | 14.68 | 27.00 | 27.87 | 26.50 | -| 128 | 32 | 20.55 | 21.01 | 19.88 | 34.51 | 34.81 | 33.83 | -| 128 | 64 | 40.48 | 41.29 | 39.87 | 67.84 | 68.57 | 67.03 | -| 128 | 128 | 82.17 | 82.53 | 80.95 | 132.78 | 133.23 | 131.64 | -| 384 | 1 | 2.75 | 2.78 | 2.67 | 3.73 | 3.79 | 3.63 | -| 384 | 2 | 4.22 | 4.38 | 4.09 | 6.68 | 7.27 | 6.53 | -| 384 | 4 | 7.87 | 8.07 | 7.75 | 13.22 | 13.50 | 12.83 | -| 384 | 8 | 16.07 | 16.13 | 15.77 | 28.01 | 28.72 | 27.48 | -| 384 | 12 | 23.87 | 24.15 | 23.53 | 40.96 | 41.51 | 39.39 | -| 384 | 16 | 31.87 | 32.25 | 30.99 | 51.56 | 51.83 | 51.00 | -| 384 | 24 | 48.14 | 48.33 | 47.22 | 82.06 | 82.56 | 80.13 | -| 384 | 32 | 64.07 | 64.48 | 63.19 | 102.64 | 103.33 | 101.20 | -| 384 | 64 | 129.58 | 130.37 | 125.79 | 215.79 | 216.38 | 213.87 | -| 384 | 128 | 258.69 | 259.74 | 245.91 | 414.96 | 415.57 | 413.16 | - -##### BERT Large - -| Sequence Length | Batch Size | INT8 Latency (ms) | | | FP16 Latency (ms) | | | -|-----------------|------------|-----------------|-----------------|---------|-----------------|-----------------|---------| -| | | 95th Percentile | 99th Percentile | Average | 95th Percentile | 99th Percentile | Average | -| 128 | 1 | 4.20 | 4.35 | 4.10 | 5.05 | 5.21 | 4.91 | -| 128 | 2 | 5.41 | 5.70 | 5.30 | 7.99 | 8.31 | 7.79 | -| 128 | 4 | 8.48 | 8.68 | 8.32 | 14.87 | 15.28 | 14.44 | -| 128 | 8 | 15.20 | 15.22 | 14.91 | 29.66 | 30.20 | 28.97 | -| 128 | 12 | 23.54 | 23.72 | 23.21 | 45.48 | 45.90 | 44.91 | -| 128 | 16 | 31.04 | 31.38 | 30.46 | 62.06 | 62.61 | 60.27 | -| 128 | 24 | 48.00 | 48.59 | 47.44 | 84.17 | 84.50 | 83.43 | -| 128 | 32 | 64.41 | 64.77 | 63.54 | 113.60 | 113.98 | 112.32 | -| 128 | 64 | 128.03 | 128.45 | 126.36 | 223.89 | 224.83 | 220.75 | -| 128 | 128 | 246.96 | 247.80 | 245.00 | 441.52 | 442.26 | 439.65 | -| 384 | 1 | 7.88 | 8.06 | 7.73 | 11.84 | 12.11 | 11.51 | -| 384 | 2 | 13.00 | 13.18 | 12.80 | 23.59 | 24.13 | 23.12 | -| 384 | 4 | 25.14 | 25.32 | 24.70 | 46.66 | 46.69 | 45.81 | -| 384 | 8 | 50.14 | 50.65 | 49.41 | 86.74 | 87.47 | 85.40 | -| 384 | 12 | 72.92 | 73.01 | 71.86 | 127.10 | 127.44 | 125.66 | -| 384 | 16 | 97.00 | 97.26 | 95.47 | 169.41 | 169.93 | 167.55 | -| 384 | 24 | 149.70 | 150.28 | 148.00 | 258.26 | 258.88 | 255.79 | -| 384 | 32 | 192.74 | 193.85 | 190.59 | 339.87 | 340.55 | 337.86 | -| 384 | 64 | 385.85 | 387.66 | 383.62 | 692.10 | 692.88 | 689.73 | -| 384 | 128 | 780.95 | 781.81 | 778.82 | 1367.61 | 1368.85 | 1365.16 | - - -#### Inference performance: NVIDIA V100 (16GB) - -Our results were obtained by running the `scripts/inference_benchmark.sh --gpu Volta` script in the container generated by the TensorRT OSS Dockerfile on NVIDIA V100 with (1x V100 16G) GPUs. - -##### BERT Base - -| Sequence Length | Batch Size | INT8 Latency (ms) | | | FP16 Latency (ms) | | | -|-----------------|------------|-----------------|-----------------|---------|-----------------|-----------------|---------| -| | | 95th Percentile | 99th Percentile | Average | 95th Percentile | 99th Percentile | Average | -| 128 | 1 | 1.39 | 1.39 | 1.39 | 1.23 | 1.23 | 1.23 | -| 128 | 2 | 1.76 | 1.76 | 1.75 | 1.49 | 1.49 | 1.48 | -| 128 | 4 | 2.35 | 2.36 | 2.34 | 2.12 | 2.13 | 2.11 | -| 128 | 8 | 3.69 | 3.7 | 3.65 | 3.35 | 3.36 | 3.32 | -| 128 | 12 | 4.79 | 4.83 | 4.75 | 4.65 | 4.67 | 4.61 | -| 128 | 16 | 6.7 | 6.72 | 6.64 | 6.3 | 6.35 | 6.25 | -| 128 | 24 | 8.95 | 8.96 | 8.9 | 8.68 | 8.71 | 8.6 | -| 128 | 32 | 14.74 | 14.77 | 14.59 | 14.16 | 14.18 | 14.06 | -| 128 | 64 | 24.12 | 24.14 | 23.98 | 22.57 | 22.63 | 22.47 | -| 128 | 128 | 45.59 | 45.65 | 45.53 | 43.45 | 43.51 | 43.25 | -| 384 | 1 | 2.17 | 2.18 | 2.16 | 1.98 | 1.98 | 1.97 | -| 384 | 2 | 3.4 | 3.42 | 3.38 | 3.11 | 3.11 | 3.08 | -| 384 | 4 | 5.61 | 5.62 | 5.57 | 5.5 | 5.52 | 5.46 | -| 384 | 8 | 10.58 | 10.63 | 10.49 | 10.26 | 10.29 | 10.17 | -| 384 | 12 | 16.55 | 16.57 | 16.43 | 15.8 | 15.83 | 15.69 | -| 384 | 16 | 21.15 | 21.19 | 21.04 | 20.09 | 20.12 | 19.94 | -| 384 | 24 | 30.95 | 31 | 30.77 | 29.44 | 29.51 | 29.24 | -| 384 | 32 | 47.94 | 48.03 | 47.66 | 47.97 | 48.05 | 47.56 | -| 384 | 64 | 81.8 | 81.91 | 81.62 | 76.84 | 77.05 | 76.4 | -| 384 | 128 | 159.87 | 160.06 | 159.47 | 151.4 | 151.61 | 150.85 | - -##### BERT Large - -| Sequence Length | Batch Size | INT8 Latency (ms) | | | FP16 Latency (ms) | | | -|-----------------|------------|-----------------|-----------------|---------|-----------------|-----------------|---------| -| | | 95th Percentile | 99th Percentile | Average | 95th Percentile | 99th Percentile | Average | -| 128 | 1 | 3.43 | 3.44 | 3.42 | 3.06 | 3.07 | 3.05 | -| 128 | 2 | 4.35 | 4.37 | 4.33 | 3.79 | 3.8 | 3.79 | -| 128 | 4 | 6.8 | 6.83 | 6.74 | 6.02 | 6.05 | 5.98 | -| 128 | 8 | 11 | 11.07 | 10.93 | 10.57 | 10.62 | 10.47 | -| 128 | 12 | 16.28 | 16.31 | 16.15 | 15.06 | 15.1 | 14.96 | -| 128 | 16 | 20.33 | 20.44 | 20.13 | 20.47 | 20.51 | 20.25 | -| 128 | 24 | 30.63 | 30.66 | 30.33 | 28.65 | 28.8 | 28.48 | -| 128 | 32 | 45.28 | 45.35 | 45.09 | 46.88 | 47.02 | 46.43 | -| 128 | 64 | 75.33 | 75.57 | 74.82 | 71.88 | 71.97 | 71.47 | -| 128 | 128 | 148.1 | 148.31 | 147.59 | 140.81 | 140.97 | 140.35 | -| 384 | 1 | 6.16 | 6.17 | 6.12 | 5.7 | 5.72 | 5.66 | -| 384 | 2 | 10.25 | 10.27 | 10.18 | 9.46 | 9.49 | 9.37 | -| 384 | 4 | 18.44 | 18.5 | 18.27 | 17.22 | 17.28 | 17.09 | -| 384 | 8 | 34.67 | 34.71 | 34.41 | 32.71 | 32.79 | 32.45 | -| 384 | 12 | 49.04 | 49.13 | 48.79 | 47.53 | 47.77 | 47.27 | -| 384 | 16 | 67.08 | 67.21 | 66.75 | 62.86 | 63.01 | 62.76 | -| 384 | 24 | 94.22 | 94.39 | 94.04 | 92.08 | 92.2 | 91.86 | -| 384 | 32 | 148.96 | 149.11 | 148.59 | 147.7 | 147.84 | 147.23 | -| 384 | 64 | 245.91 | 246.09 | 244.67 | 240.16 | 240.43 | 239.07 | - -## Experimental -### Variable sequence length -In our prior implementation, we used inputs padded to max length along with corresponding input masks to handle variable sequence length inputs in a batch. The padding results in some wasted computations which can be avoided by handling variable sequence length inputs natively. Now we have a new approach called the variable sequence length method. By concatenating each input id into a single long input id, and concatenating each input segment id into a single long segment id, TensorRT can know the exact starts and ends by providing an extra sequence length buffer that contains the start and end positions of each sequence. Now we can eliminate the wasted computation in the input paddings. - -Note this is an experimental feature because we only support Xavier+ GPUs, also there is neither FP32 support nor INT8 PTQ calibration. - -#### Run command lines - -1. Download checkpoint for BERT Large FP16 SQuAD v1.1 model with sequence length of 384: - ```bash - bash scripts/download_model.sh pyt v1_1 - ``` - -2. Build an engine: - - **FP16 engine** - ```bash - mkdir -p /workspace/TensorRT/demo/BERT/engines && python3 builder_varseqlen.py -x /workspace/TensorRT/demo/BERT/models/fine-tuned/bert_pyt_onnx_large_qa_squad11_amp_fake_quant_v1/bert_large_v1_1_fake_quant.onnx -o /workspace/TensorRT/demo/BERT/engines/bert_varseq_fp16.engine -b 1 -s 64 --fp16 -c /workspace/TensorRT/demo/BERT/models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_384_v19.03.1 -v /workspace/TensorRT/demo/BERT/models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_384_v19.03.1/vocab.txt - ``` - - This will build and engine with a maximum batch size of 1 (`-b 1`) and sequence length of 64 (`-s 64`) using FP16 precision computation where possible (`--fp16`). - - - **INT8 engine** - ```bash - mkdir -p /workspace/TensorRT/demo/BERT/engines && python3 builder_varseqlen.py -x /workspace/TensorRT/demo/BERT/models/fine-tuned/bert_pyt_onnx_large_qa_squad11_amp_fake_quant_v1/bert_large_v1_1_fake_quant.onnx -o /workspace/TensorRT/demo/BERT/engines/bert_varseq_int8.engine -b 1 -s 256 --int8 --fp16 -c /workspace/TensorRT/demo/BERT/models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_384_v19.03.1 -v /workspace/TensorRT/demo/BERT/models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_384_v19.03.1/vocab.txt - ``` - - This will build and engine with a maximum batch size of 1 (`-b 1`) and sequence length of 256 (`-s 256`) using INT8 precision computation where possible (`--int8`). - -3. Run inference - - Evaluate the F1 score and exact match score using the squad dataset: - - ```bash - python3 inference_varseqlen.py -e /workspace/TensorRT/demo/BERT/engines/bert_varseq_int8.engine -s 256 -sq ./squad/dev-v1.1.json -v /workspace/TensorRT/demo/BERT/models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_384_v19.03.1/vocab.txt -o ./predictions.json - python3 squad/evaluate-v1.1.py squad/dev-v1.1.json ./predictions.json 90 - ``` - - Run the quesion and answer mode: - - ```bash - python3 inference_varseqlen.py -e /workspace/TensorRT/demo/BERT/engines/bert_varseq_int8.engine -p "TensorRT is a high performance deep learning inference platform that delivers low latency and high throughput for apps such as recommenders, speech and image/video on NVIDIA GPUs. It includes parsers to import models, and plugins to support novel ops and layers before applying optimizations for inference. Today NVIDIA is open-sourcing parsers and plugins in TensorRT so that the deep learning community can customize and extend these components to take advantage of powerful TensorRT optimizations for your apps." -q "What is TensorRT?" -v /workspace/TensorRT/demo/BERT/models/fine-tuned/bert_tf_ckpt_large_qa_squad2_amp_384_v19.03.1/vocab.txt -s 256 - ``` - -3. Collect performance data - - ```bash - python3 perf_varseqlen.py -e /workspace/TensorRT/demo/BERT/engines/bert_varseq_int8.engine -b 1 -s 256 - ``` - - This will collect performance data run use batch size 1 (`-b 1`) and sequence length of 256 (`-s 256`). - -4. Collect performance data with CUDA graph enabled - - We can use the same `inference_c.py` and `build/perf` to collect performance data with cuda graph enabled. The command line is the same as run without variable sequence length. - +To be published soon. diff --git a/demo/BERT/builder.py b/demo/BERT/builder.py old mode 100644 new mode 100755 index 6408d748..eba4739c --- a/demo/BERT/builder.py +++ b/demo/BERT/builder.py @@ -27,17 +27,13 @@ import time import onnx import pycuda.autoinit -# Tensorflow v1 compatibility mode -try: - import tensorflow.compat.v1 as tf - tf.disable_v2_behavior() -except ImportError as err: - sys.stderr.write("""Error: Failed to import tensorflow module ({})\n""".format(err)) - sys.exit() - # TensorRT import tensorrt as trt from helpers.calibrator import BertCalibrator as BertCalibrator +from builder_utils import load_tf_weights, load_pytorch_weights_and_quant, load_onnx_weights_and_quant +from builder_utils import WQKV, BQKV # Attention Keys +from builder_utils import W_AOUT, B_AOUT, W_MID, B_MID, W_LOUT, B_LOUT # Transformer Keys +from builder_utils import SQD_W, SQD_B # SQuAD Output Keys """ TensorRT Initialization @@ -56,40 +52,8 @@ qkv2_plg_creator = plg_registry.get_plugin_creator("CustomQKVToContextPluginDyna skln_plg_creator = plg_registry.get_plugin_creator("CustomSkipLayerNormPluginDynamic", "1", "") fc_plg_creator = plg_registry.get_plugin_creator("CustomFCPluginDynamic", "1", "") -""" -Attentions Keys -""" -WQ = "self_query_kernel" -BQ = "self_query_bias" -WK = "self_key_kernel" -BK = "self_key_bias" -WV = "self_value_kernel" -BV = "self_value_bias" -WQKV = "self_qkv_kernel" -BQKV = "self_qkv_bias" - -""" -Transformer Keys -""" -W_AOUT = "attention_output_dense_kernel" -B_AOUT = "attention_output_dense_bias" -AOUT_LN_BETA = "attention_output_layernorm_beta" -AOUT_LN_GAMMA = "attention_output_layernorm_gamma" -W_MID = "intermediate_dense_kernel" -B_MID = "intermediate_dense_bias" -W_LOUT = "output_dense_kernel" -B_LOUT = "output_dense_bias" -LOUT_LN_BETA = "output_layernorm_beta" -LOUT_LN_GAMMA = "output_layernorm_gamma" - -""" -Squad Output Keys -""" -SQD_W = "squad_output_weights" -SQD_B = "squad_output_bias" - class BertConfig: - def __init__(self, bert_config_path, use_fp16, use_int8, use_strict, use_fc2_gemm, use_int8_skipln, use_int8_multihead, use_qat, timing_cache): + def __init__(self, bert_config_path, use_fp16, use_int8, use_strict, use_fc2_gemm, use_int8_skipln, use_int8_multihead, use_qat, use_sparsity, timing_cache): with open(bert_config_path, "r") as f: data = json.load(f) self.num_attention_heads = data["num_attention_heads"] @@ -105,6 +69,7 @@ class BertConfig: self.use_int8_multihead = use_int8_multihead self.is_calib_mode = False self.use_qat = use_qat + self.use_sparsity = use_sparsity self.timing_cache = timing_cache def set_tensor_name(tensor, prefix, name): @@ -139,7 +104,7 @@ def attention_layer_opt(prefix, config, init_dict, network, input_tensor, imask) # FC_attention if config.use_int8: - mult_all = network.add_convolution(input_tensor, 3 * hidden_size, (1, 1), Wall, Ball) + mult_all = network.add_convolution_nd(input_tensor, 3 * hidden_size, (1, 1), Wall, Ball) else: mult_all = network.add_fully_connected(input_tensor, 3 * hidden_size, Wall, Ball) @@ -245,7 +210,7 @@ def transformer_layer_opt(prefix, config, init_dict, network, input_tensor, imas B_aout = init_dict[prefix + B_AOUT] if config.use_int8: W_aout = init_dict[prefix + W_AOUT] - attention_out_fc = network.add_convolution(attention_heads, hidden_size, (1, 1), W_aout, B_aout) + attention_out_fc = network.add_convolution_nd(attention_heads, hidden_size, (1, 1), W_aout, B_aout) B_aout = None if not config.use_int8_skipln: @@ -268,7 +233,7 @@ def transformer_layer_opt(prefix, config, init_dict, network, input_tensor, imas B_mid = init_dict[prefix + B_MID] W_mid = init_dict[prefix + W_MID] if config.use_int8: - mid_dense = network.add_convolution(attention_ln, config.intermediate_size, (1, 1), W_mid, B_mid) + mid_dense = network.add_convolution_nd(attention_ln, config.intermediate_size, (1, 1), W_mid, B_mid) else: mid_dense = network.add_fully_connected(attention_ln, config.intermediate_size, W_mid, B_mid) @@ -305,7 +270,7 @@ def transformer_layer_opt(prefix, config, init_dict, network, input_tensor, imas B_lout = init_dict[prefix + B_LOUT] if config.use_int8 and not config.use_fc2_gemm: W_lout = init_dict[prefix + W_LOUT] - out_dense = network.add_convolution(intermediate_act, hidden_size, (1, 1), W_lout, B_lout) + out_dense = network.add_convolution_nd(intermediate_act, hidden_size, (1, 1), W_lout, B_lout) B_lout = None if not config.use_int8_skipln: @@ -359,176 +324,6 @@ def squad_output(prefix, config, init_dict, network, input_tensor): set_output_name(OUT, prefix, "squad_logits") return OUT -def load_tf_weights(inputbase, config): - """ - Load the weights from the tensorflow checkpoint - """ - weights_dict = dict() - - try: - reader = tf.train.NewCheckpointReader(inputbase) - tensor_dict = reader.get_variable_to_shape_map() - - # There might be training-related variables in the checkpoint that can be discarded - param_names = [key for key in sorted(tensor_dict) if "adam" not in key and "global_step" not in key and "pooler" not in key] - count = len(param_names) - TRT_LOGGER.log(TRT_LOGGER.INFO, "Found {:} entries in weight map".format(count)) - - for pn in param_names: - toks = pn.lower().split("/") - if "encoder" in pn: - assert ("layer" in pn) - l = (re.findall("\d+", pn))[0] - outname = "l{}_".format(l) + "_".join(toks[3:]) - else: - outname = "_".join(toks) - - tensor = reader.get_tensor(pn) - shape = tensor.shape - if pn.find("kernel") != -1: - weights_dict[outname + "_notrans"] = trt.Weights(np.ascontiguousarray(tensor).flatten()) - - TRT_LOGGER.log(TRT_LOGGER.VERBOSE, "Transposing {}\n".format(np)) - tensor = np.transpose(tensor) - - shape = tensor.shape - flat_tensor = tensor.flatten() - shape_str = "{} ".format(len(shape)) + " ".join([str(d) for d in shape]) - weights_dict[outname] = trt.Weights(flat_tensor) - - TRT_LOGGER.log(TRT_LOGGER.VERBOSE, "Original name: {:}, TensorRT name: {:}, shape: {:}".format(pn, outname, shape_str)) - - N = config.num_attention_heads - H = config.head_size - - additional_dict = dict() - for key, value in weights_dict.items(): - pos = key.find(BQ) - if pos != -1: - hidden_size = value.size - prefix = key[:pos] - - Bq_ = value - Bk_ = weights_dict[prefix + BK] - Bv_ = weights_dict[prefix + BV] - Wq_ = weights_dict[prefix + WQ] - Wk_ = weights_dict[prefix + WK] - Wv_ = weights_dict[prefix + WV] - - mat_size = hidden_size * hidden_size - wcount = 3 * mat_size - Wall = np.zeros(wcount, np.float32) - bcount = 3 * hidden_size - Ball = np.zeros(bcount, np.float32) - Wall[0:mat_size] = Wq_.numpy()[0:mat_size] - Wall[mat_size:2*mat_size] = Wk_.numpy()[0:mat_size] - Wall[2*mat_size:3*mat_size] = Wv_.numpy()[0:mat_size] - Ball[0:hidden_size] = Bq_.numpy()[0:hidden_size] - Ball[hidden_size:2*hidden_size] = Bk_.numpy()[0:hidden_size] - Ball[2*hidden_size:3*hidden_size] = Bv_.numpy()[0:hidden_size] - - Wall = np.ascontiguousarray(Wall.reshape((3, N, H, N, H)).transpose((1, 0, 2, 3, 4)), dtype=np.float32) - Ball = np.ascontiguousarray(Ball.reshape((3, N, H)).transpose((1, 0, 2)), dtype=np.float32) - - additional_dict[prefix + WQKV] = trt.Weights(Wall) - additional_dict[prefix + BQKV] = trt.Weights(Ball) - - additional_dict[prefix + WQKV + "_notrans"] = trt.Weights(Wall.T) - - except Exception as error: - TRT_LOGGER.log(TRT_LOGGER.ERROR, str(error)) - - weights_dict.update(additional_dict) - return weights_dict - -def onnx_to_trt_name(onnx_name): - """ - Converting variables in the onnx checkpoint to names corresponding to the naming convention used in the TF version, expected by the builder - """ - onnx_name = onnx_name.lower() - toks = [t.strip('_') for t in onnx_name.split('.')] - if toks[0] == 'bert': #embeddings or encoder - if toks[1] == 'encoder': #transformer - - if toks[-2] == 'layernorm': #bias->beta, weight->gamma - toks[-1] = 'beta' if toks[-1] == 'bias' else 'gamma' - elif (toks[-2] == 'dense' or toks[-2] in {'key', 'value', 'query'}) and toks[-1] == 'weight': - toks[-1] = 'kernel' - elif (toks[-3] == 'dense' or toks[-3] in {'key', 'value', 'query'}) and toks[-1] == 'amax': - if toks[-2] == 'weight_quantizer': - toks[-2] = 'kernel' - elif toks[-2] == 'input_quantizer': - toks[-2] = 'input' - - if 'final_input_quantizer' not in toks[2]: - toks = toks[3:] - toks[0] = 'l{}'.format(int(toks[0])) - else: - if toks[-2] == 'layernorm': #bias->beta, weight->gamma - toks[-1] = 'beta' if toks[-1] == 'bias' else 'gamma' - else: #embeddings: drop "_weight" suffix - if toks[-1] == 'amax': - toks[-2] = 'amax' - toks = toks[:-1] - elif 'qa' in onnx_name: - name = 'cls_squad_output_bias' if toks[-1] == 'bias' else 'cls_squad_output_weights' - return name - else: - print("Encountered unknown case:", onnx_name) - assert(False) - parsed = '_'.join(toks) - return parsed - -def load_onnx_weights_and_quant(path, config): - """ - Load the weights from the onnx checkpoint - """ - N = config.num_attention_heads - H = config.head_size - hidden_size = config.hidden_size - - model = onnx.load(path) - weights = model.graph.initializer - tensor_dict = dict([(onnx_to_trt_name(w.name), np.frombuffer(w.raw_data, np.float32).reshape(w.dims)) for w in weights]) - - weights_dict = dict() - for outname, tensor in tensor_dict.items(): - if outname.find("_amax") != -1: - weights_dict[outname] = tensor - elif outname.find(BQ) != -1: - prefix = outname[:outname.find(BQ)] - - Wqkv = np.zeros((3, hidden_size, hidden_size), np.float32) - Bqkv = np.zeros((3, hidden_size), np.float32) - - Wqkv[0,:,:] = tensor_dict[prefix + WQ] - Wqkv[1,:,:] = tensor_dict[prefix + WK] - Wqkv[2,:,:] = tensor_dict[prefix + WV] - Bqkv[0,:] = tensor - Bqkv[1,:] = tensor_dict[prefix + BK] - Bqkv[2,:] = tensor_dict[prefix + BV] - - Wqkv = np.ascontiguousarray(Wqkv.reshape((3, N, H, N, H)).transpose((1,0,2,3,4))) - Bqkv = np.ascontiguousarray(Bqkv.reshape((3, N, H)).transpose((1,0,2))) - - weights_dict[prefix + WQKV] = trt.Weights(Wqkv) - weights_dict[prefix + BQKV] = trt.Weights(Bqkv) - weights_dict[prefix + WQKV + "_notrans"] = trt.Weights(Wqkv.T) - - elif outname.find(BK) != -1 or outname.find(BV) != -1 or outname.find(WQ) != -1 or outname.find(WK) != -1 or outname.find(WV) != -1: - pass - else: - flat_tensor = np.ascontiguousarray(tensor).flatten() - weights_dict[outname] = trt.Weights(flat_tensor) - - if outname.find("kernel") != -1: - tensor = np.transpose(tensor) - weights_dict[outname + "_notrans"] = trt.Weights(np.ascontiguousarray(tensor).flatten()) - - - TRT_LOGGER.log(TRT_LOGGER.INFO, "Found {:} entries in weight map".format(len(weights_dict))) - return weights_dict - def emb_layernorm(builder, network, config, weights_dict, builder_config, sequence_lengths, batch_sizes): # int8 only support some of the sequence length, we dynamic on sequence length is not allowed. input_ids = network.add_input(name="input_ids", dtype=trt.int32, shape=(-1, -1 if len(sequence_lengths) > 1 else sequence_lengths[0])) @@ -600,6 +395,10 @@ def build_engine(batch_sizes, workspace_size, sequence_lengths, config, weights_ builder_config.int8_calibrator = calibrator if config.use_strict: builder_config.set_flag(trt.BuilderFlag.STRICT_TYPES) + + if config.use_sparsity: + TRT_LOGGER.log(TRT_LOGGER.INFO, "Setting sparsity flag on builder_config.") + builder_config.set_flag(trt.BuilderFlag.SPARSE_WEIGHTS) # speed up the engine build for trt major version >= 8 # 1. disable cudnn tactic @@ -678,6 +477,7 @@ def main(): parser.add_argument("-m", "--ckpt", required=False, help="The checkpoint file basename, e.g.: basename(model.ckpt-766908.data-00000-of-00001) is model.ckpt-766908") parser.add_argument("-x", "--onnx", required=False, help="The ONNX model file path.") + parser.add_argument("-pt", "--pytorch", required=False, help="The PyTorch checkpoint file path.") parser.add_argument("-o", "--output", required=True, default="bert_base_384.engine", help="The bert engine file, ex bert.engine") parser.add_argument("-b", "--batch-size", default=[], action="append", help="Batch size(s) to optimize for. The engine will be usable with any batch size below this, but may not be optimal for smaller sizes. Can be specified multiple times to optimize for more than one batch size.", type=int) parser.add_argument("-s", "--sequence-length", default=[], action="append", help="Sequence length of the BERT model", type=int) @@ -694,6 +494,7 @@ def main(): parser.add_argument("-g", "--force-fc2-gemm", action="store_true", help="Force use gemm to implement FC2 layer", required=False) parser.add_argument("-iln", "--force-int8-skipln", action="store_true", help="Run skip layernorm with INT8 (FP32 or FP16 by default) inputs and output", required=False) parser.add_argument("-imh", "--force-int8-multihead", action="store_true", help="Run multi-head attention with INT8 (FP32 or FP16 by default) input and output", required=False) + parser.add_argument("-sp", "--sparse", action="store_true", help="Indicates that model is sparse", required=False) parser.add_argument("-tcf", "--timing-cache-file", help="Path to tensorrt build timeing cache file, only available for tensorrt 8.0 and later", required=False) args, _ = parser.parse_known_args() @@ -709,7 +510,7 @@ def main(): bert_config_path = os.path.join(args.config_dir, "bert_config.json") TRT_LOGGER.log(TRT_LOGGER.INFO, "Using configuration file: {:}".format(bert_config_path)) - config = BertConfig(bert_config_path, args.fp16, args.int8, args.strict, args.force_fc2_gemm, args.force_int8_skipln, args.force_int8_multihead, args.int8 and args.onnx != None, args.timing_cache_file) + config = BertConfig(bert_config_path, args.fp16, args.int8, args.strict, args.force_fc2_gemm, args.force_int8_skipln, args.force_int8_multihead, args.int8 and args.onnx != None, args.sparse, args.timing_cache_file) if args.calib_path != None: calib_cache = args.calib_path @@ -718,6 +519,8 @@ def main(): if args.onnx != None: weights_dict = load_onnx_weights_and_quant(args.onnx, config) + elif args.pytorch != None: + weights_dict = load_pytorch_weights_and_quant(args.pytorch, config) elif args.ckpt != None: weights_dict = load_tf_weights(args.ckpt, config) generate_calibration_cache(args.sequence_length, args.workspace_size, config, weights_dict, args.squad_json, args.vocab_file, calib_cache, args.calib_num) diff --git a/demo/BERT/builder_utils.py b/demo/BERT/builder_utils.py new file mode 100644 index 00000000..897fbc92 --- /dev/null +++ b/demo/BERT/builder_utils.py @@ -0,0 +1,298 @@ +import re +import pickle + +import numpy as np +import onnx +import torch +import tensorrt as trt + +try: + import tensorflow.compat.v1 as tf + tf.disable_v2_behavior() +except ImportError as err: + sys.stderr.write("""Error: Failed to import tensorflow module ({})\n""".format(err)) + sys.exit() + +TRT_LOGGER = trt.Logger(trt.Logger.INFO) + +""" +Attentions Keys +""" +WQ = "self_query_kernel" +BQ = "self_query_bias" +WK = "self_key_kernel" +BK = "self_key_bias" +WV = "self_value_kernel" +BV = "self_value_bias" +WQKV = "self_qkv_kernel" +BQKV = "self_qkv_bias" + +""" +Transformer Keys +""" +W_AOUT = "attention_output_dense_kernel" +B_AOUT = "attention_output_dense_bias" +AOUT_LN_BETA = "attention_output_layernorm_beta" +AOUT_LN_GAMMA = "attention_output_layernorm_gamma" +W_MID = "intermediate_dense_kernel" +B_MID = "intermediate_dense_bias" +W_LOUT = "output_dense_kernel" +B_LOUT = "output_dense_bias" +LOUT_LN_BETA = "output_layernorm_beta" +LOUT_LN_GAMMA = "output_layernorm_gamma" + +""" +Squad Output Keys +""" +SQD_W = "squad_output_weights" +SQD_B = "squad_output_bias" + + +def load_tf_weights(inputbase, config): + """ + Load the weights from the tensorflow checkpoint + """ + weights_dict = dict() + + try: + reader = tf.train.NewCheckpointReader(inputbase) + tensor_dict = reader.get_variable_to_shape_map() + + # There might be training-related variables in the checkpoint that can be discarded + param_names = [key for key in sorted(tensor_dict) if "adam" not in key and "global_step" not in key and "pooler" not in key] + count = len(param_names) + TRT_LOGGER.log(TRT_LOGGER.INFO, "Found {:} entries in weight map".format(count)) + + for pn in param_names: + toks = pn.lower().split("/") + if "encoder" in pn: + assert ("layer" in pn) + l = (re.findall("\d+", pn))[0] + outname = "l{}_".format(l) + "_".join(toks[3:]) + else: + outname = "_".join(toks) + + tensor = reader.get_tensor(pn) + shape = tensor.shape + if pn.find("kernel") != -1: + weights_dict[outname + "_notrans"] = trt.Weights(np.ascontiguousarray(tensor).flatten()) + + TRT_LOGGER.log(TRT_LOGGER.VERBOSE, "Transposing {}\n".format(np)) + tensor = np.transpose(tensor) + + shape = tensor.shape + flat_tensor = tensor.flatten() + shape_str = "{} ".format(len(shape)) + " ".join([str(d) for d in shape]) + weights_dict[outname] = trt.Weights(flat_tensor) + + TRT_LOGGER.log(TRT_LOGGER.VERBOSE, "Original name: {:}, TensorRT name: {:}, shape: {:}".format(pn, outname, shape_str)) + + N = config.num_attention_heads + H = config.head_size + + additional_dict = dict() + for key, value in weights_dict.items(): + pos = key.find(BQ) + if pos != -1: + hidden_size = value.size + prefix = key[:pos] + + Bq_ = value + Bk_ = weights_dict[prefix + BK] + Bv_ = weights_dict[prefix + BV] + Wq_ = weights_dict[prefix + WQ] + Wk_ = weights_dict[prefix + WK] + Wv_ = weights_dict[prefix + WV] + + mat_size = hidden_size * hidden_size + wcount = 3 * mat_size + Wall = np.zeros(wcount, np.float32) + bcount = 3 * hidden_size + Ball = np.zeros(bcount, np.float32) + Wall[0:mat_size] = Wq_.numpy()[0:mat_size] + Wall[mat_size:2*mat_size] = Wk_.numpy()[0:mat_size] + Wall[2*mat_size:3*mat_size] = Wv_.numpy()[0:mat_size] + Ball[0:hidden_size] = Bq_.numpy()[0:hidden_size] + Ball[hidden_size:2*hidden_size] = Bk_.numpy()[0:hidden_size] + Ball[2*hidden_size:3*hidden_size] = Bv_.numpy()[0:hidden_size] + + if config.use_int8 and getattr(config, 'interleaved', False): + Wall = np.ascontiguousarray(Wall.reshape((3, N, H, N, H)), dtype=np.float32) + Ball = np.ascontiguousarray(Ball.reshape((3, N, H)), dtype=np.float32) + else: + Wall = np.ascontiguousarray(Wall.reshape((3, N, H, N, H)).transpose((1, 0, 2, 3, 4)), dtype=np.float32) + Ball = np.ascontiguousarray(Ball.reshape((3, N, H)).transpose((1, 0, 2)), dtype=np.float32) + + additional_dict[prefix + WQKV] = trt.Weights(Wall) + additional_dict[prefix + BQKV] = trt.Weights(Ball) + + additional_dict[prefix + WQKV + "_notrans"] = trt.Weights(Wall.T) + + except Exception as error: + TRT_LOGGER.log(TRT_LOGGER.ERROR, str(error)) + + weights_dict.update(additional_dict) + return weights_dict + +def onnx_to_trt_name(onnx_name): + """ + Converting variables in the onnx checkpoint to names corresponding to the naming convention used in the TF version, expected by the builder + """ + qkv_strings = {'key', 'value', 'query', 'query_key_value'} + onnx_name = onnx_name.lower() + toks = [t.strip('_') for t in onnx_name.split('.')] + if toks[0] == 'bert': #embeddings or encoder + if toks[1] == 'encoder': #transformer + # Token conversions for sparse checkpoints + if toks[-2] == 'dense_act': + toks[-2] = 'dense' + elif toks[-3] == 'dense_act': + if toks[-2] == 'input_quantizer': + toks[-2] = 'input' + elif toks[-2] == 'weight_quantizer': + toks[-2] = 'kernel' + toks[-3] = 'dense' + elif toks[-2].startswith('matmul'): + toks[-2] = { + 'matmul_q_quantizer': 'qv_a_input_quantizer', + 'matmul_k_quantizer': 'qv_b_input_quantizer', + 'matmul_v_quantizer': 'av_b_input_quantizer', + 'matmul_a_quantizer': 'av_a_input_quantizer', + }[toks[-2].replace('input_', '')] + + # Token conversions for all checkpoints + if toks[-2] == 'layernorm': #bias->beta, weight->gamma + toks[-1] = 'beta' if toks[-1] == 'bias' else 'gamma' + elif (toks[-2] == 'dense' or toks[-2] in qkv_strings) and toks[-1] == 'weight': + toks[-1] = 'kernel' + elif (toks[-3] == 'dense' or toks[-3] in qkv_strings) and toks[-1] == 'amax': + if toks[-2] == 'weight_quantizer': + toks[-2] = 'kernel' + elif toks[-2] == 'input_quantizer': + toks[-2] = 'input' + + if 'final_input_quantizer' not in toks[2]: + ind = toks.index('layers')+1 if 'layers' in toks else 3 + toks = toks[ind:] + toks[0] = 'l{}'.format(int(toks[0])) + else: + if toks[-2] == 'layernorm': #bias->beta, weight->gamma + toks[-1] = 'beta' if toks[-1] == 'bias' else 'gamma' + else: #embeddings: drop "_weight" suffix + if toks[-1] == 'amax': + toks[-2] = 'amax' + toks = toks[:-1] + elif 'qa' in onnx_name: + name = 'cls_squad_output_bias' if toks[-1] == 'bias' else 'cls_squad_output_weights' + return name + else: + print("Encountered unknown case:", onnx_name) + assert(False) + parsed = '_'.join(toks) + return parsed + +def get_onnx_weight_dict(tensor_dict, config): + N = config.num_attention_heads + H = config.head_size + hidden_size = config.hidden_size + + weights_dict = dict() + for outname, tensor in tensor_dict.items(): + if outname.find("_amax") != -1: + weights_dict[outname] = tensor + elif outname.find(BQ) != -1: + prefix = outname[:outname.find(BQ)] + + Wqkv = np.zeros((3, hidden_size, hidden_size), np.float32) + Bqkv = np.zeros((3, hidden_size), np.float32) + + Wqkv[0,:,:] = tensor_dict[prefix + WQ] + Wqkv[1,:,:] = tensor_dict[prefix + WK] + Wqkv[2,:,:] = tensor_dict[prefix + WV] + Bqkv[0,:] = tensor + Bqkv[1,:] = tensor_dict[prefix + BK] + Bqkv[2,:] = tensor_dict[prefix + BV] + + if config.use_int8 and getattr(config, 'interleaved', False): + Wqkv = np.ascontiguousarray(Wqkv.reshape((3, N, H, N, H))) + Bqkv = np.ascontiguousarray(Bqkv.reshape((3, N, H))) + else: + Wqkv = np.ascontiguousarray(Wqkv.reshape((3, N, H, N, H)).transpose((1,0,2,3,4))) + Bqkv = np.ascontiguousarray(Bqkv.reshape((3, N, H)).transpose((1,0,2))) + + weights_dict[prefix + WQKV] = trt.Weights(Wqkv) + weights_dict[prefix + BQKV] = trt.Weights(Bqkv) + weights_dict[prefix + WQKV + "_notrans"] = trt.Weights(Wqkv.T) + + elif outname.find(BK) != -1 or outname.find(BV) != -1 or outname.find(WQ) != -1 or outname.find(WK) != -1 or outname.find(WV) != -1: + pass + else: + flat_tensor = np.ascontiguousarray(tensor).flatten() + weights_dict[outname] = trt.Weights(flat_tensor) + + if outname.find("kernel") != -1: + tensor = np.transpose(tensor) + weights_dict[outname + "_notrans"] = trt.Weights(np.ascontiguousarray(tensor).flatten()) + + TRT_LOGGER.log(TRT_LOGGER.INFO, "Found {:} entries in weight map".format(len(weights_dict))) + return weights_dict + +def load_onnx_weights_and_quant(path, config): + """ + Load the weights from the onnx checkpoint + """ + model = onnx.load(path) + weights = model.graph.initializer + tensor_dict = dict((onnx_to_trt_name(w.name), np.frombuffer(w.raw_data, np.int8).reshape(w.dims)) + if w.name.split('_')[-1] == 'mask' else + (onnx_to_trt_name(w.name), np.frombuffer(w.raw_data, np.float32).reshape(w.dims)) + for w in weights) + return get_onnx_weight_dict(tensor_dict, config) + +def load_pytorch_weights_and_quant(path, config): + """ + Load the weights from the pytorch checkpoint + """ + state_dict = torch.load(path, map_location='cpu')["model"] + tensor_dict = {onnx_to_trt_name(name):val.numpy() for name, val in state_dict.items()} + return get_onnx_weight_dict(tensor_dict, config) + +def load_megatron_pickle_weights(path, config): + N = config.num_attention_heads + H = config.head_size + + with open(path, 'rb') as f: + tensor_dict = pickle.load(f) + + weight_dict = {} + for name, tensor in tensor_dict.items(): + if 'scale' in name: + continue + + name = (onnx_to_trt_name(name) + .replace('embedding_', 'embeddings_') + .replace('tokentype_', 'token_type_') + .replace('_av', '_self_av') + .replace('_qv', '_self_qv') + .replace('query_key_value', 'self_qkv')) + + if name.endswith('self_qkv_kernel'): + tensor = np.ascontiguousarray(tensor.reshape((3, N, H, N, H))).astype(np.float32) + weight_dict[name] = trt.Weights(tensor) + elif name.endswith('self_qkv_bias'): + tensor = np.ascontiguousarray(tensor.reshape((3, N, H))).astype(np.float32) + weight_dict[name] = trt.Weights(tensor) + elif name == 'l{}_output_layernorm_output_quantizer_amax'.format(config.num_hidden_layers-1): + weight_dict['bert_encoder_final_input_quantizer_amax'] = tensor + elif name.endswith('_amax'): + weight_dict[name] = tensor + if name.endswith('_qkv_input_amax'): + weight_dict[name.replace('_qkv_input_amax', '_query_input_amax')] = tensor + weight_dict[name.replace('_qkv_input_amax', '_key_input_amax')] = tensor + weight_dict[name.replace('_qkv_input_amax', '_value_input_amax')] = tensor + else: + flat_tensor = np.ascontiguousarray(tensor).flatten().astype(np.float32) + weight_dict[name] = trt.Weights(flat_tensor) + + TRT_LOGGER.log(TRT_LOGGER.INFO, "Found {:} entries in weight map".format(len(weight_dict))) + return weight_dict diff --git a/demo/BERT/builder_varseqlen.py b/demo/BERT/builder_varseqlen.py old mode 100644 new mode 100755 index 4e88399e..be8e9b37 --- a/demo/BERT/builder_varseqlen.py +++ b/demo/BERT/builder_varseqlen.py @@ -29,12 +29,10 @@ import pycuda.autoinit # TensorRT import tensorrt as trt - -try: - from tensorflow.python import pywrap_tensorflow as pyTF -except ImportError as err: - sys.stderr.write("""Error: Failed to import tensorflow module ({})\n""".format(err)) - sys.exit() +from builder_utils import load_tf_weights, load_pytorch_weights_and_quant, load_onnx_weights_and_quant, load_megatron_pickle_weights +from builder_utils import WQKV, BQKV # Attention Keys +from builder_utils import W_AOUT, B_AOUT, W_MID, B_MID, W_LOUT, B_LOUT # Transformer Keys +from builder_utils import SQD_W, SQD_B # SQuAD Output Keys """ TensorRT Initialization @@ -55,40 +53,12 @@ skln_plg_creator2 = plg_registry.get_plugin_creator("CustomSkipLayerNormPluginDy mha_plg_creator3 = plg_registry.get_plugin_creator("CustomQKVToContextPluginDynamic", "3", "") skln_plg_creator3 = plg_registry.get_plugin_creator("CustomSkipLayerNormPluginDynamic", "3", "") -""" -Attentions Keys -""" -WQ = "self_query_kernel" -BQ = "self_query_bias" -WK = "self_key_kernel" -BK = "self_key_bias" -WV = "self_value_kernel" -BV = "self_value_bias" -WQKV = "self_qkv_kernel" -BQKV = "self_qkv_bias" - -""" -Transformer Keys -""" -W_AOUT = "attention_output_dense_kernel" -B_AOUT = "attention_output_dense_bias" -AOUT_LN_BETA = "attention_output_layernorm_beta" -AOUT_LN_GAMMA = "attention_output_layernorm_gamma" -W_MID = "intermediate_dense_kernel" -B_MID = "intermediate_dense_bias" -W_LOUT = "output_dense_kernel" -B_LOUT = "output_dense_bias" -LOUT_LN_BETA = "output_layernorm_beta" -LOUT_LN_GAMMA = "output_layernorm_gamma" - -""" -Squad Output Keys -""" -SQD_W = "squad_output_weights" -SQD_B = "squad_output_bias" +# Megatron Plugins +emln_plg_creator3 = plg_registry.get_plugin_creator("CustomEmbLayerNormPluginDynamic", "3", "") +skln_plg_creator4 = plg_registry.get_plugin_creator("CustomSkipLayerNormPluginDynamic", "4", "") class BertConfig: - def __init__(self, bert_config_path, use_fp16, use_int8, use_qat, interleaved, timing_cache): + def __init__(self, bert_config_path, use_fp16, use_int8, use_qat, interleaved, timing_cache, use_sparsity, use_megatron): with open(bert_config_path, "r") as f: data = json.load(f) self.num_attention_heads = data["num_attention_heads"] @@ -101,6 +71,8 @@ class BertConfig: self.use_qat = use_qat self.interleaved = interleaved self.timing_cache = timing_cache + self.use_sparsity = use_sparsity + self.use_megatron = use_megatron def get_trt_dtype(self): dtype = trt.float32 @@ -132,7 +104,7 @@ def attention_layer_opt(prefix, config, init_dict, network, input_tensor, mask_i # FC_attention if config.use_int8: - mult_all = network.add_convolution(input_tensor, 3 * hidden_size, (1, 1), Wall, Ball) + mult_all = network.add_convolution_nd(input_tensor, 3 * hidden_size, (1, 1), Wall, Ball) else: mult_all = network.add_fully_connected(input_tensor, 3 * hidden_size, Wall, Ball) @@ -182,7 +154,7 @@ def attention_layer_opt(prefix, config, init_dict, network, input_tensor, mask_i set_output_name(qkv2ctx, prefix, "context_layer") return qkv2ctx -def skipln(prefix, config, init_dict, network, input_tensor, skip): +def skipln(prefix, config, init_dict, network, input_tensor, skip, is_last_skipln=False): """ Add the skip layer """ @@ -198,7 +170,8 @@ def skipln(prefix, config, init_dict, network, input_tensor, skip): if config.use_int8 and config.interleaved: pfc = trt.PluginFieldCollection([pf_beta, pf_gamma]) - skipln_plug = skln_plg_creator3.create_plugin("skipln", pfc) + creator = skln_plg_creator3 if not config.use_megatron or is_last_skipln else skln_plg_creator4 + skipln_plug = creator.create_plugin("skipln", pfc) else: pfc = trt.PluginFieldCollection([pf_ld, pf_beta, pf_gamma, pf_type]) skipln_plug = skln_plg_creator2.create_plugin("skipln", pfc) @@ -207,7 +180,7 @@ def skipln(prefix, config, init_dict, network, input_tensor, skip): layer = network.add_plugin_v2(skipln_inputs, skipln_plug) return layer -def transformer_layer_opt(prefix, config, init_dict, network, input_tensor, mask_idx, cu_seqlens, max_seqlen): +def transformer_layer_opt(prefix, config, init_dict, network, input_tensor, residual, mask_idx, cu_seqlens, max_seqlen): """ Add the transformer layer """ @@ -226,14 +199,20 @@ def transformer_layer_opt(prefix, config, init_dict, network, input_tensor, mask B_aout = init_dict[prefix + B_AOUT] W_aout = init_dict[prefix + W_AOUT] if config.use_int8: - attention_out_fc = network.add_convolution(attention_heads, hidden_size, (1, 1), W_aout, B_aout) + attention_out_fc = network.add_convolution_nd(attention_heads, hidden_size, (1, 1), W_aout, B_aout) else: attention_out_fc = network.add_fully_connected(attention_heads, hidden_size, W_aout, B_aout) if config.use_int8 and config.use_qat: dr_fc_aout = init_dict[prefix + 'attention_output_add_local_input_quantizer_amax'] set_output_range(attention_out_fc, dr_fc_aout) - skiplayer = skipln(prefix + "attention_output_layernorm_", config, init_dict, network, attention_out_fc.get_output(0), input_tensor) + if config.use_megatron: + dr_skln1_res_in = init_dict[prefix + "attention_output_add_residual_input_quantizer_amax"] + residual.set_dynamic_range(-dr_skln1_res_in, dr_skln1_res_in) + skip = residual + else: + skip = input_tensor + skiplayer = skipln(prefix + "attention_output_layernorm_", config, init_dict, network, attention_out_fc.get_output(0), skip) attention_ln = skiplayer.get_output(0) if config.use_qat: dr_skln1 = init_dict[prefix + 'intermediate_dense_input_amax'] @@ -243,27 +222,11 @@ def transformer_layer_opt(prefix, config, init_dict, network, input_tensor, mask B_mid = init_dict[prefix + B_MID] W_mid = init_dict[prefix + W_MID] if config.use_int8: - mid_dense = network.add_convolution(attention_ln, config.intermediate_size, (1, 1), W_mid, B_mid) + mid_dense = network.add_convolution_nd(attention_ln, config.intermediate_size, (1, 1), W_mid, B_mid) else: mid_dense = network.add_fully_connected(attention_ln, config.intermediate_size, W_mid, B_mid) - mid_dense_out = mid_dense.get_output(0) - POW = network.add_constant((1,) * len(mid_dense_out.shape), trt.Weights(np.ascontiguousarray([3.0], dtype=np.float32))) - MULTIPLY = network.add_constant((1,) * len(mid_dense_out.shape), trt.Weights(np.ascontiguousarray([0.044715], dtype=np.float32))) - SQRT = network.add_constant((1,) * len(mid_dense_out.shape), trt.Weights((np.ascontiguousarray([0.79788456080286535587989211986876], dtype=np.float32)))) - ONE = network.add_constant((1,) * len(mid_dense_out.shape), trt.Weights((np.ascontiguousarray([1.0], dtype=np.float32)))) - HALF = network.add_constant((1,) * len(mid_dense_out.shape), trt.Weights((np.ascontiguousarray([0.5], dtype=np.float32)))) - X_pow = network.add_elementwise(mid_dense_out, POW.get_output(0), trt.ElementWiseOperation.POW) - X_pow_t = X_pow.get_output(0) - X_mul = network.add_elementwise(X_pow_t, MULTIPLY.get_output(0), trt.ElementWiseOperation.PROD) - X_add = network.add_elementwise(mid_dense_out, X_mul.get_output(0), trt.ElementWiseOperation.SUM) - X_sqrt = network.add_elementwise(X_add.get_output(0), SQRT.get_output(0), trt.ElementWiseOperation.PROD) - X_sqrt_tensor = X_sqrt.get_output(0) - X_tanh = network.add_activation(X_sqrt_tensor, trt.ActivationType.TANH) - X_tanh_tensor = X_tanh.get_output(0) - X_one = network.add_elementwise(X_tanh_tensor, ONE.get_output(0), trt.ElementWiseOperation.SUM) - CDF = network.add_elementwise(X_one.get_output(0), HALF.get_output(0), trt.ElementWiseOperation.PROD) - gelu_layer = network.add_elementwise(CDF.get_output(0), mid_dense_out, trt.ElementWiseOperation.PROD) + gelu_layer = add_gelu(network, mid_dense.get_output(0)) intermediate_act = gelu_layer.get_output(0) set_tensor_name(intermediate_act, prefix, "gelu") @@ -281,7 +244,7 @@ def transformer_layer_opt(prefix, config, init_dict, network, input_tensor, mask W_lout = init_dict[prefix + W_LOUT] if config.use_int8: - out_dense = network.add_convolution(intermediate_act, hidden_size, (1, 1), W_lout, B_lout) + out_dense = network.add_convolution_nd(intermediate_act, hidden_size, (1, 1), W_lout, B_lout) else: out_dense = network.add_fully_connected(intermediate_act, hidden_size, W_lout, B_lout) if config.use_int8 and config.use_qat: @@ -289,25 +252,79 @@ def transformer_layer_opt(prefix, config, init_dict, network, input_tensor, mask set_output_range(out_dense, dr_fc_out) set_output_name(out_dense, prefix + "output_", "dense") - out_layer = skipln(prefix + "output_layernorm_", config, init_dict, network, out_dense.get_output(0), attention_ln) + if config.use_megatron: + dr_skln2_res_in = init_dict[prefix + 'output_add_residual_input_quantizer_amax'] + set_output_range(skiplayer, dr_skln2_res_in, out_idx=1) + skip = skiplayer.get_output(1) + else: + skip = attention_ln + + is_last_skipln = prefix.startswith('l{}'.format(config.num_hidden_layers-1)) + out_layer = skipln(prefix + "output_layernorm_", config, init_dict, network, out_dense.get_output(0), skip, is_last_skipln) set_output_name(out_layer, prefix + "output_", "reshape") return out_layer -def bert_model(config, init_dict, network, input_tensor, mask_idx, cu_seqlens, max_seqlen): +def add_gelu(network, input_tensor): + """ + Adds elementwise GELU, and will trigger FC+GELU fusion in TRT + """ + shape = (1, ) * len(input_tensor.shape) + POW = network.add_constant(shape, trt.Weights(np.ascontiguousarray([3.0], dtype=np.float32))) + MULTIPLY = network.add_constant(shape, trt.Weights(np.ascontiguousarray([0.044715], dtype=np.float32))) + SQRT = network.add_constant(shape, trt.Weights((np.ascontiguousarray([0.79788456080286535587989211986876], dtype=np.float32)))) + ONE = network.add_constant(shape, trt.Weights((np.ascontiguousarray([1.0], dtype=np.float32)))) + HALF = network.add_constant(shape, trt.Weights((np.ascontiguousarray([0.5], dtype=np.float32)))) + X_pow = network.add_elementwise(input_tensor, POW.get_output(0), trt.ElementWiseOperation.POW) + X_pow_t = X_pow.get_output(0) + X_mul = network.add_elementwise(X_pow_t, MULTIPLY.get_output(0), trt.ElementWiseOperation.PROD) + X_add = network.add_elementwise(input_tensor, X_mul.get_output(0), trt.ElementWiseOperation.SUM) + X_sqrt = network.add_elementwise(X_add.get_output(0), SQRT.get_output(0), trt.ElementWiseOperation.PROD) + X_sqrt_tensor = X_sqrt.get_output(0) + X_tanh = network.add_activation(X_sqrt_tensor, trt.ActivationType.TANH) + X_tanh_tensor = X_tanh.get_output(0) + X_one = network.add_elementwise(X_tanh_tensor, ONE.get_output(0), trt.ElementWiseOperation.SUM) + CDF = network.add_elementwise(X_one.get_output(0), HALF.get_output(0), trt.ElementWiseOperation.PROD) + gelu_layer = network.add_elementwise(CDF.get_output(0), input_tensor, trt.ElementWiseOperation.PROD) + + # enable elementwise fusing for int8 && fp16 + POW.precision = trt.DataType.FLOAT + MULTIPLY.precision = trt.DataType.FLOAT + SQRT.precision = trt.DataType.FLOAT + ONE.precision = trt.DataType.FLOAT + HALF.precision = trt.DataType.FLOAT + X_pow.precision = trt.DataType.FLOAT + X_mul.precision = trt.DataType.FLOAT + X_add.precision = trt.DataType.FLOAT + X_sqrt.precision = trt.DataType.FLOAT + X_tanh.precision = trt.DataType.FLOAT + X_one.precision = trt.DataType.FLOAT + CDF.precision = trt.DataType.FLOAT + gelu_layer.precision = trt.DataType.FLOAT + return gelu_layer + + +def bert_model(config, init_dict, network, input_tensor, residual, mask_idx, cu_seqlens, max_seqlen): """ Create the bert model """ prev_input = input_tensor for layer in range(0, config.num_hidden_layers): ss = "l{}_".format(layer) - out_layer = transformer_layer_opt(ss, config, init_dict, network, prev_input, mask_idx, cu_seqlens, max_seqlen) + out_layer = transformer_layer_opt(ss, config, init_dict, network, prev_input, residual, mask_idx, cu_seqlens, max_seqlen) prev_input = out_layer.get_output(0) + # Skip reading residual from final layer + if config.use_megatron and (layer != config.num_hidden_layers - 1): + residual = out_layer.get_output(1) if config.use_qat: dr_out = init_dict["bert_encoder_final_input_quantizer_amax"] set_output_range(out_layer, dr_out) - return prev_input + + squad_logits = squad_output("cls_", config, init_dict, network, prev_input) + squad_logits_out = squad_logits.get_output(0) + network.mark_output(squad_logits_out) + def squad_output(prefix, config, init_dict, network, input_tensor): """ @@ -319,7 +336,7 @@ def squad_output(prefix, config, init_dict, network, input_tensor): B_out = init_dict[prefix + SQD_B] if config.use_int8: - dense = network.add_convolution(input_tensor, 2, (1, 1), W_out, B_out) + dense = network.add_convolution_nd(input_tensor, 2, (1, 1), W_out, B_out) else: dense = network.add_fully_connected(input_tensor, 2, W_out, B_out) @@ -331,184 +348,6 @@ def squad_output(prefix, config, init_dict, network, input_tensor): set_output_name(OUT, prefix, "squad_logits") return OUT -def load_tf_weights(inputbase, config): - """ - Load the weights from the tensorflow checkpoint - """ - weights_dict = dict() - - try: - reader = pyTF.NewCheckpointReader(inputbase) - tensor_dict = reader.get_variable_to_shape_map() - - # There might be training-related variables in the checkpoint that can be discarded - param_names = [key for key in sorted(tensor_dict) if "adam" not in key and "global_step" not in key and "pooler" not in key] - count = len(param_names) - TRT_LOGGER.log(TRT_LOGGER.INFO, "Found {:} entries in weight map".format(count)) - - for pn in param_names: - toks = pn.lower().split("/") - if "encoder" in pn: - assert ("layer" in pn) - l = (re.findall("\d+", pn))[0] - outname = "l{}_".format(l) + "_".join(toks[3:]) - else: - outname = "_".join(toks) - - tensor = reader.get_tensor(pn) - shape = tensor.shape - if pn.find("kernel") != -1: - weights_dict[outname + "_notrans"] = trt.Weights(np.ascontiguousarray(tensor).flatten()) - - TRT_LOGGER.log(TRT_LOGGER.VERBOSE, "Transposing {}\n".format(np)) - tensor = np.transpose(tensor) - - shape = tensor.shape - flat_tensor = tensor.flatten() - shape_str = "{} ".format(len(shape)) + " ".join([str(d) for d in shape]) - weights_dict[outname] = trt.Weights(flat_tensor) - - TRT_LOGGER.log(TRT_LOGGER.VERBOSE, "Original name: {:}, TensorRT name: {:}, shape: {:}".format(pn, outname, shape_str)) - - N = config.num_attention_heads - H = config.head_size - - additional_dict = dict() - for key, value in weights_dict.items(): - pos = key.find(BQ) - if pos != -1: - hidden_size = value.size - prefix = key[:pos] - - Bq_ = value - Bk_ = weights_dict[prefix + BK] - Bv_ = weights_dict[prefix + BV] - Wq_ = weights_dict[prefix + WQ] - Wk_ = weights_dict[prefix + WK] - Wv_ = weights_dict[prefix + WV] - - mat_size = hidden_size * hidden_size - wcount = 3 * mat_size - Wall = np.zeros(wcount, np.float32) - bcount = 3 * hidden_size - Ball = np.zeros(bcount, np.float32) - Wall[0:mat_size] = Wq_.numpy()[0:mat_size] - Wall[mat_size:2*mat_size] = Wk_.numpy()[0:mat_size] - Wall[2*mat_size:3*mat_size] = Wv_.numpy()[0:mat_size] - Ball[0:hidden_size] = Bq_.numpy()[0:hidden_size] - Ball[hidden_size:2*hidden_size] = Bk_.numpy()[0:hidden_size] - Ball[2*hidden_size:3*hidden_size] = Bv_.numpy()[0:hidden_size] - - if config.use_int8 and config.interleaved: - Wall = np.ascontiguousarray(Wall.reshape((3, N, H, N, H)), dtype=np.float32) - Ball = np.ascontiguousarray(Ball.reshape((3, N, H)), dtype=np.float32) - else: - Wall = np.ascontiguousarray(Wall.reshape((3, N, H, N, H)).transpose((1, 0, 2, 3, 4)), dtype=np.float32) - Ball = np.ascontiguousarray(Ball.reshape((3, N, H)).transpose((1, 0, 2)), dtype=np.float32) - - additional_dict[prefix + WQKV] = trt.Weights(Wall) - additional_dict[prefix + BQKV] = trt.Weights(Ball) - - additional_dict[prefix + WQKV + "_notrans"] = trt.Weights(Wall.T) - - except Exception as error: - TRT_LOGGER.log(TRT_LOGGER.ERROR, str(error)) - - weights_dict.update(additional_dict) - return weights_dict - -def onnx_to_trt_name(onnx_name): - """ - Converting variables in the onnx checkpoint to names corresponding to the naming convention used in the TF version, expected by the builder - """ - onnx_name = onnx_name.lower() - toks = [t.strip('_') for t in onnx_name.split('.')] - if toks[0] == 'bert': #embeddings or encoder - if toks[1] == 'encoder': #transformer - - if toks[-2] == 'layernorm': #bias->beta, weight->gamma - toks[-1] = 'beta' if toks[-1] == 'bias' else 'gamma' - elif (toks[-2] == 'dense' or toks[-2] in {'key', 'value', 'query'}) and toks[-1] == 'weight': - toks[-1] = 'kernel' - elif (toks[-3] == 'dense' or toks[-3] in {'key', 'value', 'query'}) and toks[-1] == 'amax': - if toks[-2] == 'weight_quantizer': - toks[-2] = 'kernel' - elif toks[-2] == 'input_quantizer': - toks[-2] = 'input' - - if 'final_input_quantizer' not in toks[2]: - toks = toks[3:] - toks[0] = 'l{}'.format(int(toks[0])) - else: - if toks[-2] == 'layernorm': #bias->beta, weight->gamma - toks[-1] = 'beta' if toks[-1] == 'bias' else 'gamma' - else: #embeddings: drop "_weight" suffix - if toks[-1] == 'amax': - toks[-2] = 'amax' - toks = toks[:-1] - elif 'qa' in onnx_name: - name = 'cls_squad_output_bias' if toks[-1] == 'bias' else 'cls_squad_output_weights' - return name - else: - print("Encountered unknown case:", onnx_name) - assert(False) - parsed = '_'.join(toks) - return parsed - -def load_onnx_weights_and_quant(path, config): - """ - Load the weights from the onnx checkpoint - """ - N = config.num_attention_heads - H = config.head_size - hidden_size = config.hidden_size - - model = onnx.load(path) - weights = model.graph.initializer - tensor_dict = dict([(onnx_to_trt_name(w.name), np.frombuffer(w.raw_data, np.float32).reshape(w.dims)) for w in weights]) - - weights_dict = dict() - for outname, tensor in tensor_dict.items(): - if outname.find("_amax") != -1: - weights_dict[outname] = tensor - elif outname.find(BQ) != -1: - prefix = outname[:outname.find(BQ)] - - Wqkv = np.zeros((3, hidden_size, hidden_size), np.float32) - Bqkv = np.zeros((3, hidden_size), np.float32) - - Wqkv[0,:,:] = tensor_dict[prefix + WQ] - Wqkv[1,:,:] = tensor_dict[prefix + WK] - Wqkv[2,:,:] = tensor_dict[prefix + WV] - Bqkv[0,:] = tensor - Bqkv[1,:] = tensor_dict[prefix + BK] - Bqkv[2,:] = tensor_dict[prefix + BV] - - if config.use_int8 and config.interleaved: - Wqkv = np.ascontiguousarray(Wqkv.reshape((3, N, H, N, H))) - Bqkv = np.ascontiguousarray(Bqkv.reshape((3, N, H))) - else: - Wqkv = np.ascontiguousarray(Wqkv.reshape((3, N, H, N, H)).transpose((1,0,2,3,4))) - Bqkv = np.ascontiguousarray(Bqkv.reshape((3, N, H)).transpose((1,0,2))) - - weights_dict[prefix + WQKV] = trt.Weights(Wqkv) - weights_dict[prefix + BQKV] = trt.Weights(Bqkv) - weights_dict[prefix + WQKV + "_notrans"] = trt.Weights(Wqkv.T) - - elif outname.find(BK) != -1 or outname.find(BV) != -1 or outname.find(WQ) != -1 or outname.find(WK) != -1 or outname.find(WV) != -1: - pass - else: - flat_tensor = np.ascontiguousarray(tensor).flatten() - weights_dict[outname] = trt.Weights(flat_tensor) - - if outname.find("kernel") != -1: - tensor = np.transpose(tensor) - weights_dict[outname + "_notrans"] = trt.Weights(np.ascontiguousarray(tensor).flatten()) - - - TRT_LOGGER.log(TRT_LOGGER.INFO, "Found {:} entries in weight map".format(len(weights_dict))) - return weights_dict - def emb_layernorm(builder, network, config, weights_dict, builder_config, max_sequence_length, max_batch_size): input_ids = network.add_input(name="input_ids", dtype=trt.int32, shape=(-1,)) segment_ids = network.add_input(name="segment_ids", dtype=trt.int32, shape=(-1,)) @@ -530,18 +369,22 @@ def emb_layernorm(builder, network, config, weights_dict, builder_config, max_se wwordemb = trt.PluginField("bert_embeddings_word_embeddings", weights_dict["bert_embeddings_word_embeddings"].numpy(), trt.PluginFieldType.FLOAT32) wtokemb = trt.PluginField("bert_embeddings_token_type_embeddings", weights_dict["bert_embeddings_token_type_embeddings"].numpy(), trt.PluginFieldType.FLOAT32) wposemb = trt.PluginField("bert_embeddings_position_embeddings", weights_dict["bert_embeddings_position_embeddings"].numpy(), trt.PluginFieldType.FLOAT32) - output_fp16 = trt.PluginField("output_fp16", np.array([1 if config.use_fp16 or config.use_int8 else 0]).astype(np.int32), trt.PluginFieldType.INT32) pfc = trt.PluginFieldCollection([wbeta, wgamma, wwordemb, wtokemb, wposemb, output_fp16]) - fn = emln_plg_creator2.create_plugin("embeddings", pfc) + fn = (emln_plg_creator3 if config.use_megatron else emln_plg_creator2).create_plugin("embeddings", pfc) inputs = [input_ids, segment_ids, cu_seqlens, max_seqlen] emb_layer = network.add_plugin_v2(inputs, fn) if config.use_int8 and config.use_qat: dr_input = weights_dict['l0_attention_self_query_input_amax'] - set_output_range(emb_layer, dr_input) + set_output_range(emb_layer, dr_input, out_idx=0) + + if config.use_megatron: + dr_skln1_res_in = weights_dict['l0_attention_output_add_residual_input_quantizer_amax'] + set_output_range(emb_layer, dr_skln1_res_in, out_idx=1) + set_output_name(emb_layer, "embeddings_", "output") return emb_layer, cu_seqlens, max_seqlen @@ -572,6 +415,9 @@ def build_engine(batch_size, workspace_size, sequence_length, config, weights_di cache = builder_config.create_timing_cache(b"") builder_config.set_timing_cache(cache, ignore_mismatch = False) + if config.use_sparsity: + TRT_LOGGER.log(TRT_LOGGER.INFO, "Setting sparsity flag on builder_config.") + builder_config.set_flag(trt.BuilderFlag.SPARSE_WEIGHTS) # Create the network emb_layer, cu_seqlens, max_seqlen = emb_layernorm(builder, network, config, weights_dict, builder_config, sequence_length, batch_size) @@ -583,12 +429,20 @@ def build_engine(batch_size, workspace_size, sequence_length, config, weights_di mask_idx = None else: mask_idx = emb_layer.get_output(1) + + if config.use_megatron: # megatron currently only supports int8 and interleaved + shuffler = network.add_shuffle(emb_layer.get_output(1)) + shuffler.second_transpose = (2, 1, 0, 3) + residual = shuffler.get_output(0) - bert_out = bert_model(config, weights_dict, network, embeddings, mask_idx, cu_seqlens, max_seqlen) + dr_emb = weights_dict['l0_attention_self_query_input_amax'] + embeddings.set_dynamic_range(-dr_emb, dr_emb) + dr_skln1_res_in = weights_dict['l0_attention_output_add_residual_input_quantizer_amax'] + residual.set_dynamic_range(-dr_skln1_res_in, dr_skln1_res_in) + else: + residual = None - squad_logits = squad_output("cls_", config, weights_dict, network, bert_out) - squad_logits_out = squad_logits.get_output(0) - network.mark_output(squad_logits_out) + bert_model(config, weights_dict, network, embeddings, residual, mask_idx, cu_seqlens, max_seqlen) build_start_time = time.time() engine = builder.build_engine(network, builder_config) @@ -611,6 +465,8 @@ def main(): parser.add_argument("-m", "--ckpt", required=False, help="The checkpoint file basename, e.g.: basename(model.ckpt-766908.data-00000-of-00001) is model.ckpt-766908") parser.add_argument("-x", "--onnx", required=False, help="The ONNX model file path.") + parser.add_argument("-pt", "--pytorch", required=False, help="The PyTorch checkpoint file path.") + parser.add_argument("-pkl", "--pickle", required=False, help="The Pickle weights dictionary file path for the Megatron variant of BERT.") parser.add_argument("-o", "--output", required=True, default="bert_base_384.engine", help="The bert engine file, ex bert.engine") parser.add_argument("-b", "--max-batch-size", default=1, help="Max batch size. The engine will be usable with any input with (batch-size * sequence-length) below (max-batch-size * max-sequence-length).", type=int) parser.add_argument("-s", "--max-sequence-length", default=128, help="Max sequence length of the BERT model. The engine will be usable with any input with (batch-size * sequence-length) below (max-batch-size * max-sequence-length).", type=int) @@ -625,17 +481,25 @@ def main(): parser.add_argument("-p", "--calib-path", help="calibration cache path", required=False) parser.add_argument("-il", "--interleaved", action="store_true", help="use interleaved format, only valid in INT8 precision", required=False) parser.add_argument("-tcf", "--timing-cache-file", help="Path to tensorrt build timeing cache file, only available for tensorrt 8.0 and later", required=False) + parser.add_argument("-sp", "--sparse", action="store_true", help="Indicates that model is sparse", required=False) + parser.add_argument("--megatron", action="store_true", help="Indicates that model is the Megatron-style architecture", required=False) args, _ = parser.parse_known_args() cc = pycuda.autoinit.device.compute_capability() if cc[0] * 10 + cc[1] < 72: raise RuntimeError("This variable-length BERT demo only support Xavier+ GPU.") + + if args.megatron: + if not (args.interleaved and args.int8): + raise RuntimeError("Megatron BERT currently only supports int8 and interleaved.") + if not args.pickle: + raise RuntimeError("Megatron BERT currently only supports loading a pickle weights dictionary.") bert_config_path = os.path.join(args.config_dir, "bert_config.json") TRT_LOGGER.log(TRT_LOGGER.INFO, "Using configuration file: {:}".format(bert_config_path)) - config = BertConfig(bert_config_path, args.fp16, args.int8, args.int8 and args.onnx != None, args.interleaved, args.timing_cache_file) + config = BertConfig(bert_config_path, args.fp16, args.int8, args.int8 and (args.onnx or args.pytorch or args.pickle), args.interleaved, args.timing_cache_file, args.sparse, args.megatron) if args.calib_path != None: calib_cache = args.calib_path @@ -644,10 +508,16 @@ def main(): if args.onnx != None: weights_dict = load_onnx_weights_and_quant(args.onnx, config) + elif args.pytorch != None: + weights_dict = load_pytorch_weights_and_quant(args.pytorch, config) elif args.ckpt != None: weights_dict = load_tf_weights(args.ckpt, config) + elif args.pickle != None: + weights_dict = load_megatron_pickle_weights(args.pickle, config) else: - raise RuntimeError("You need either specify TF checkpoint using option --ckpt or ONNX using option --onnx to build TRT BERT model.") + raise RuntimeError("You need either specify TF checkpoint using option --ckpt, ONNX using option --onnx, " + "PyTorch using option --pytorch, or Pickle weight dictionary using option --pickle " + "to build TRT BERT model.") with build_engine(args.max_batch_size, args.workspace_size, args.max_sequence_length, config, weights_dict, args.squad_json, args.vocab_file, calib_cache, args.calib_num) as engine: TRT_LOGGER.log(TRT_LOGGER.VERBOSE, "Serializing Engine...") diff --git a/demo/BERT/infer_c/bert_infer.h b/demo/BERT/infer_c/bert_infer.h index f50776b9..f4fba2f8 100644 --- a/demo/BERT/infer_c/bert_infer.h +++ b/demo/BERT/infer_c/bert_infer.h @@ -318,7 +318,7 @@ struct BertInference gLogInfo << "\tAverage Time: " << avgTime << " ms\n"; gLogInfo << "\t95th Percentile Time: " << percentile95 << " ms\n"; gLogInfo << "\t99th Percentile Time: " << percentile99 << " ms\n"; - gLogInfo << "\tThroughtput: " << throughput << " sentences/s\n"; + gLogInfo << "\tThroughput: " << throughput << " sentences/s\n"; } ~BertInference() diff --git a/demo/BERT/infer_c/logging.h b/demo/BERT/infer_c/logging.h index c89e827a..d4d51099 100644 --- a/demo/BERT/infer_c/logging.h +++ b/demo/BERT/infer_c/logging.h @@ -66,7 +66,7 @@ public: } private: - void log(Severity severity, const char* msg) override + void log(Severity severity, const char* msg) noexcept override { report(severity, msg) << "\n"; } diff --git a/demo/BERT/inference.py b/demo/BERT/inference.py index ab4a7123..96c9e812 100644 --- a/demo/BERT/inference.py +++ b/demo/BERT/inference.py @@ -76,6 +76,9 @@ def parse_args(): parser.add_argument('--n-best-size', help='Total number of n-best predictions to generate in the nbest_predictions.json output file', default=20, type=int) + parser.add_argument('--doc-stride', + help='When splitting up a long document into chunks, what stride to take between chunks', + default=128, type=int) args, _ = parser.parse_known_args() return args @@ -106,7 +109,7 @@ if __name__ == '__main__': tokenizer = tokenization.FullTokenizer(vocab_file=args.vocab_file, do_lower_case=True) # When splitting up a long document into chunks, how much stride to take between chunks. - doc_stride = 128 + doc_stride = args.doc_stride # The maximum total input sequence length after WordPiece tokenization. # Sequences longer than this will be truncated, and sequences shorter max_seq_length = args.sequence_length diff --git a/demo/BERT/inference_varseqlen.py b/demo/BERT/inference_varseqlen.py index a1d61428..a8bf3a8c 100644 --- a/demo/BERT/inference_varseqlen.py +++ b/demo/BERT/inference_varseqlen.py @@ -75,6 +75,9 @@ def parse_args(): parser.add_argument('--n-best-size', help='Total number of n-best predictions to generate in the nbest_predictions.json output file', default=20, type=int) + parser.add_argument('--doc-stride', + help='When splitting up a long document into chunks, what stride to take between chunks', + default=128, type=int) args, _ = parser.parse_known_args() return args @@ -105,7 +108,7 @@ if __name__ == '__main__': tokenizer = tokenization.FullTokenizer(vocab_file=args.vocab_file, do_lower_case=True) # When splitting up a long document into chunks, how much stride to take between chunks. - doc_stride = 128 + doc_stride = args.doc_stride # The maximum total input sequence length after WordPiece tokenization. # Sequences longer than this will be truncated, and sequences shorter max_seq_length = args.sequence_length diff --git a/demo/BERT/perf.py b/demo/BERT/perf.py index 976afea3..4851de8a 100644 --- a/demo/BERT/perf.py +++ b/demo/BERT/perf.py @@ -78,8 +78,9 @@ def main(): bench_times = {} + stream = cuda.Stream() for idx, batch_size in enumerate(sorted(args.batch_size)): - context.active_optimization_profile = idx + context.set_optimization_profile_async(idx, stream.handle) # Each profile has unique bindings binding_idx_offset = idx * num_binding_per_profile @@ -99,7 +100,6 @@ def main(): total_time = 0 start = cuda.Event() end = cuda.Event() - stream = cuda.Stream() # Warmup for _ in range(args.warm_up_runs): diff --git a/demo/BERT/scripts/download_model.sh b/demo/BERT/scripts/download_model.sh index 5339df9f..54ceaf44 100755 --- a/demo/BERT/scripts/download_model.sh +++ b/demo/BERT/scripts/download_model.sh @@ -20,17 +20,21 @@ SQUAD='2' MODEL='large' SEQ_LEN='128' FW='tf' +WTYPE='dense' +PREC='fp16' while test $# -gt 0 do case "$1" in - -h) echo "Usage: sh download_model.sh [tf|pyt] [base|large] [128|384] [v2|v1_1]" + -h) echo "Usage: sh download_model.sh [tf|pyt] [base|large|megatron-large] [128|384] [v2|v1_1] [sparse] [int8-qat]" exit 0 ;; base) MODEL='base' ;; large) MODEL='large' ;; + megatron-large) MODEL='megatron' + ;; 128) SEQ_LEN='128' ;; 384) SEQ_LEN='384' @@ -43,6 +47,10 @@ do ;; pyt) FW='pyt' ;; + int8-qat) PREC='int8qat' + ;; + sparse) WTYPE='sparse' + ;; *) echo "Invalid argument $1...exiting" exit 0 ;; @@ -56,12 +64,14 @@ pushd models/fine-tuned # Download the BERT fine-tuned model echo "Downloading BERT-${FW} ${MODEL} checkpoints for sequence length ${SEQ_LEN} and fine-tuned for SQuAD ${SQUAD}." - if [ "${FW}" = 'tf' ]; then CKPT=bert_${FW}_ckpt_${MODEL}_qa_squad${SQUAD}_amp_${SEQ_LEN} CKPT_VERSION=19.03.1 elif [ "${FW}" = 'pyt' ]; then - if [ "${MODEL}" != 'large' ] || [ "${SQUAD}" != '11' ]; then + if [ "${MODEL}" == 'megatron' ]; then + CKPT=bert_${FW}_statedict_megatron_${WTYPE}_${PREC} + CKPT_VERSION=21.03.0 + elif [ "${MODEL}" != 'large' ] || [ "${SQUAD}" != '11' ]; then echo "ERROR: Only BERT-large checkpoint fine-tuned for SQuAD v1.1 available in the QAT (PyTorch) workflow." else CKPT=bert_${FW}_onnx_${MODEL}_qa_squad${SQUAD}_amp_fake_quant @@ -78,4 +88,5 @@ if [ -n "$CKPT" ]; then ngc registry model download-version nvidia/${CKPT}:${CKPT_VERSION} fi fi + popd diff --git a/demo/BERT/squad/evaluate-v2.0.py b/demo/BERT/squad/evaluate-v2.0.py new file mode 100644 index 00000000..d034da91 --- /dev/null +++ b/demo/BERT/squad/evaluate-v2.0.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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. +# + +# Obtained from https://rajpurkar.github.io/SQuAD-explorer/ + +"""Official evaluation script for SQuAD version 2.0. + +In addition to basic functionality, we also compute additional statistics and +plot precision-recall curves if an additional na_prob.json file is provided. +This file is expected to map question ID's to the model's predicted probability +that a question is unanswerable. +""" +import argparse +import collections +import json +import numpy as np +import os +import re +import string +import sys + +OPTS = None + +def parse_args(): + parser = argparse.ArgumentParser('Official evaluation script for SQuAD version 2.0.') + parser.add_argument('data_file', metavar='data.json', help='Input data JSON file.') + parser.add_argument('pred_file', metavar='pred.json', help='Model predictions.') + parser.add_argument('--out-file', '-o', metavar='eval.json', + help='Write accuracy metrics to file (default is stdout).') + parser.add_argument('--na-prob-file', '-n', metavar='na_prob.json', + help='Model estimates of probability of no answer.') + parser.add_argument('--na-prob-thresh', '-t', type=float, default=1.0, + help='Predict "" if no-answer probability exceeds this (default = 1.0).') + parser.add_argument('--out-image-dir', '-p', metavar='out_images', default=None, + help='Save precision-recall curves to directory.') + parser.add_argument('--verbose', '-v', action='store_true') + if len(sys.argv) == 1: + parser.print_help() + sys.exit(1) + return parser.parse_args() + +def make_qid_to_has_ans(dataset): + qid_to_has_ans = {} + for article in dataset: + for p in article['paragraphs']: + for qa in p['qas']: + qid_to_has_ans[qa['id']] = bool(qa['answers']) + return qid_to_has_ans + +def normalize_answer(s): + """Lower text and remove punctuation, articles and extra whitespace.""" + def remove_articles(text): + regex = re.compile(r'\b(a|an|the)\b', re.UNICODE) + return re.sub(regex, ' ', text) + def white_space_fix(text): + return ' '.join(text.split()) + def remove_punc(text): + exclude = set(string.punctuation) + return ''.join(ch for ch in text if ch not in exclude) + def lower(text): + return text.lower() + return white_space_fix(remove_articles(remove_punc(lower(s)))) + +def get_tokens(s): + if not s: return [] + return normalize_answer(s).split() + +def compute_exact(a_gold, a_pred): + return int(normalize_answer(a_gold) == normalize_answer(a_pred)) + +def compute_f1(a_gold, a_pred): + gold_toks = get_tokens(a_gold) + pred_toks = get_tokens(a_pred) + common = collections.Counter(gold_toks) & collections.Counter(pred_toks) + num_same = sum(common.values()) + if len(gold_toks) == 0 or len(pred_toks) == 0: + # If either is no-answer, then F1 is 1 if they agree, 0 otherwise + return int(gold_toks == pred_toks) + if num_same == 0: + return 0 + precision = 1.0 * num_same / len(pred_toks) + recall = 1.0 * num_same / len(gold_toks) + f1 = (2 * precision * recall) / (precision + recall) + return f1 + +def get_raw_scores(dataset, preds): + exact_scores = {} + f1_scores = {} + for article in dataset: + for p in article['paragraphs']: + for qa in p['qas']: + qid = qa['id'] + gold_answers = [a['text'] for a in qa['answers'] + if normalize_answer(a['text'])] + if not gold_answers: + # For unanswerable questions, only correct answer is empty string + gold_answers = [''] + if qid not in preds: + print('Missing prediction for %s' % qid) + continue + a_pred = preds[qid] + # Take max over all gold answers + exact_scores[qid] = max(compute_exact(a, a_pred) for a in gold_answers) + f1_scores[qid] = max(compute_f1(a, a_pred) for a in gold_answers) + return exact_scores, f1_scores + +def apply_no_ans_threshold(scores, na_probs, qid_to_has_ans, na_prob_thresh): + new_scores = {} + for qid, s in scores.items(): + pred_na = na_probs[qid] > na_prob_thresh + if pred_na: + new_scores[qid] = float(not qid_to_has_ans[qid]) + else: + new_scores[qid] = s + return new_scores + +def make_eval_dict(exact_scores, f1_scores, qid_list=None): + if not qid_list: + total = len(exact_scores) + return collections.OrderedDict([ + ('exact', 100.0 * sum(exact_scores.values()) / total), + ('f1', 100.0 * sum(f1_scores.values()) / total), + ('total', total), + ]) + else: + total = len(qid_list) + return collections.OrderedDict([ + ('exact', 100.0 * sum(exact_scores[k] for k in qid_list) / total), + ('f1', 100.0 * sum(f1_scores[k] for k in qid_list) / total), + ('total', total), + ]) + +def merge_eval(main_eval, new_eval, prefix): + for k in new_eval: + main_eval['%s_%s' % (prefix, k)] = new_eval[k] + +def plot_pr_curve(precisions, recalls, out_image, title): + plt.step(recalls, precisions, color='b', alpha=0.2, where='post') + plt.fill_between(recalls, precisions, step='post', alpha=0.2, color='b') + plt.xlabel('Recall') + plt.ylabel('Precision') + plt.xlim([0.0, 1.05]) + plt.ylim([0.0, 1.05]) + plt.title(title) + plt.savefig(out_image) + plt.clf() + +def make_precision_recall_eval(scores, na_probs, num_true_pos, qid_to_has_ans, + out_image=None, title=None): + qid_list = sorted(na_probs, key=lambda k: na_probs[k]) + true_pos = 0.0 + cur_p = 1.0 + cur_r = 0.0 + precisions = [1.0] + recalls = [0.0] + avg_prec = 0.0 + for i, qid in enumerate(qid_list): + if qid_to_has_ans[qid]: + true_pos += scores[qid] + cur_p = true_pos / float(i+1) + cur_r = true_pos / float(num_true_pos) + if i == len(qid_list) - 1 or na_probs[qid] != na_probs[qid_list[i+1]]: + # i.e., if we can put a threshold after this point + avg_prec += cur_p * (cur_r - recalls[-1]) + precisions.append(cur_p) + recalls.append(cur_r) + if out_image: + plot_pr_curve(precisions, recalls, out_image, title) + return {'ap': 100.0 * avg_prec} + +def run_precision_recall_analysis(main_eval, exact_raw, f1_raw, na_probs, + qid_to_has_ans, out_image_dir): + if out_image_dir and not os.path.exists(out_image_dir): + os.makedirs(out_image_dir) + num_true_pos = sum(1 for v in qid_to_has_ans.values() if v) + if num_true_pos == 0: + return + pr_exact = make_precision_recall_eval( + exact_raw, na_probs, num_true_pos, qid_to_has_ans, + out_image=os.path.join(out_image_dir, 'pr_exact.png'), + title='Precision-Recall curve for Exact Match score') + pr_f1 = make_precision_recall_eval( + f1_raw, na_probs, num_true_pos, qid_to_has_ans, + out_image=os.path.join(out_image_dir, 'pr_f1.png'), + title='Precision-Recall curve for F1 score') + oracle_scores = {k: float(v) for k, v in qid_to_has_ans.items()} + pr_oracle = make_precision_recall_eval( + oracle_scores, na_probs, num_true_pos, qid_to_has_ans, + out_image=os.path.join(out_image_dir, 'pr_oracle.png'), + title='Oracle Precision-Recall curve (binary task of HasAns vs. NoAns)') + merge_eval(main_eval, pr_exact, 'pr_exact') + merge_eval(main_eval, pr_f1, 'pr_f1') + merge_eval(main_eval, pr_oracle, 'pr_oracle') + +def histogram_na_prob(na_probs, qid_list, image_dir, name): + if not qid_list: + return + x = [na_probs[k] for k in qid_list] + weights = np.ones_like(x) / float(len(x)) + plt.hist(x, weights=weights, bins=20, range=(0.0, 1.0)) + plt.xlabel('Model probability of no-answer') + plt.ylabel('Proportion of dataset') + plt.title('Histogram of no-answer probability: %s' % name) + plt.savefig(os.path.join(image_dir, 'na_prob_hist_%s.png' % name)) + plt.clf() + +def find_best_thresh(preds, scores, na_probs, qid_to_has_ans): + num_no_ans = sum(1 for k in qid_to_has_ans if not qid_to_has_ans[k]) + cur_score = num_no_ans + best_score = cur_score + best_thresh = 0.0 + qid_list = sorted(na_probs, key=lambda k: na_probs[k]) + for i, qid in enumerate(qid_list): + if qid not in scores: continue + if qid_to_has_ans[qid]: + diff = scores[qid] + else: + if preds[qid]: + diff = -1 + else: + diff = 0 + cur_score += diff + if cur_score > best_score: + best_score = cur_score + best_thresh = na_probs[qid] + return 100.0 * best_score / len(scores), best_thresh + +def find_all_best_thresh(main_eval, preds, exact_raw, f1_raw, na_probs, qid_to_has_ans): + best_exact, exact_thresh = find_best_thresh(preds, exact_raw, na_probs, qid_to_has_ans) + best_f1, f1_thresh = find_best_thresh(preds, f1_raw, na_probs, qid_to_has_ans) + main_eval['best_exact'] = best_exact + main_eval['best_exact_thresh'] = exact_thresh + main_eval['best_f1'] = best_f1 + main_eval['best_f1_thresh'] = f1_thresh + +def main(): + with open(OPTS.data_file) as f: + dataset_json = json.load(f) + dataset = dataset_json['data'] + with open(OPTS.pred_file) as f: + preds = json.load(f) + if OPTS.na_prob_file: + with open(OPTS.na_prob_file) as f: + na_probs = json.load(f) + else: + na_probs = {k: 0.0 for k in preds} + qid_to_has_ans = make_qid_to_has_ans(dataset) # maps qid to True/False + has_ans_qids = [k for k, v in qid_to_has_ans.items() if v] + no_ans_qids = [k for k, v in qid_to_has_ans.items() if not v] + exact_raw, f1_raw = get_raw_scores(dataset, preds) + exact_thresh = apply_no_ans_threshold(exact_raw, na_probs, qid_to_has_ans, + OPTS.na_prob_thresh) + f1_thresh = apply_no_ans_threshold(f1_raw, na_probs, qid_to_has_ans, + OPTS.na_prob_thresh) + out_eval = make_eval_dict(exact_thresh, f1_thresh) + if has_ans_qids: + has_ans_eval = make_eval_dict(exact_thresh, f1_thresh, qid_list=has_ans_qids) + merge_eval(out_eval, has_ans_eval, 'HasAns') + if no_ans_qids: + no_ans_eval = make_eval_dict(exact_thresh, f1_thresh, qid_list=no_ans_qids) + merge_eval(out_eval, no_ans_eval, 'NoAns') + if OPTS.na_prob_file: + find_all_best_thresh(out_eval, preds, exact_raw, f1_raw, na_probs, qid_to_has_ans) + if OPTS.na_prob_file and OPTS.out_image_dir: + run_precision_recall_analysis(out_eval, exact_raw, f1_raw, na_probs, + qid_to_has_ans, OPTS.out_image_dir) + histogram_na_prob(na_probs, has_ans_qids, OPTS.out_image_dir, 'hasAns') + histogram_na_prob(na_probs, no_ans_qids, OPTS.out_image_dir, 'noAns') + if OPTS.out_file: + with open(OPTS.out_file, 'w') as f: + json.dump(out_eval, f) + else: + print(json.dumps(out_eval, indent=2)) + +if __name__ == '__main__': + OPTS = parse_args() + if OPTS.out_image_dir: + import matplotlib + matplotlib.use('Agg') + import matplotlib.pyplot as plt + main() diff --git a/demo/Tacotron2/README.md b/demo/Tacotron2/README.md index d7f5cbeb..2dd36e15 100644 --- a/demo/Tacotron2/README.md +++ b/demo/Tacotron2/README.md @@ -48,33 +48,38 @@ Software version configuration tested for the instructions that follow: ```bash mkdir -p output - python3 exports/export_tacotron2_onnx.py --tacotron2 checkpoints/tacotron2_pyt_ckpt_amp_v19.09.0/nvidia_tacotron2pyt_fp16_20190427 -o output/ --fp16 + python tensorrt/convert_tacotron22onnx.py --tacotron2 ./checkpoints/nvidia_tacotron2pyt_fp16_20190427 -o output/ --fp16 ``` - Export WaveGlow to ONNX IR: + Convert WaveGlow to ONNX IR: ```bash - python3 exports/export_waveglow_onnx.py --waveglow checkpoints/waveglow_ckpt_amp_256_v19.10.0/nvidia_waveglow256pyt_fp16 --wn-channels 256 -o output/ --fp16 - ``` + python tensorrt/convert_waveglow2onnx.py --waveglow ./checkpoints/nvidia_waveglow256pyt_fp16 --config-file config.json --wn-channels 256 -o output/ --fp16 + ``` After running the above commands, there should be four new ONNX files in `./output/` directory: - `encoder.onnx`, `decoder_iter.onnx`, `postnet.onnx`, and `waveglow.onnx`. + `encoder.onnx`, `decoder_iter.onnx`, `postnet.onnx`, and `waveglow.onnx`. If TensorRT 8.0+ is being used and the `--no-loop` option is not specified, `decoder.onnx` will also be created. 6. Export the ONNX IRs to TensorRT engines with fp16 mode enabled: - ```bash - python3 trt/export_onnx2trt.py --encoder output/encoder.onnx --decoder output/decoder_iter.onnx --postnet output/postnet.onnx --waveglow output/waveglow.onnx -o output/ --fp16 + ```bash + python tensorrt/convert_onnx2trt.py --encoder output/encoder.onnx --decoder output/decoder_iter.onnx --postnet output/postnet.onnx --waveglow output/waveglow.onnx -o output/ --fp16 ``` After running the command, there should be four new engine files in `./output/` directory: `encoder_fp16.engine`, `decoder_iter_fp16.engine`, `postnet_fp16.engine`, and `waveglow_fp16.engine`. + For TensorRT 8.0+, use `output/decoder.onnx` for the decoder, which will output `decoder_with_outer_loop_fp16.engine`. + 7. Run TTS inference pipeline with fp16: + ```bash - python3 trt/inference_trt.py -i phrases/phrase.txt --encoder output/encoder_fp16.engine --decoder output/decoder_iter_fp16.engine --postnet output/postnet_fp16.engine --waveglow output/waveglow_fp16.engine -o output/ --fp16 + python tensorrt/inference_trt.py -i phrases/phrase.txt --encoder output/encoder_fp16.engine --decoder output/decoder_iter_fp16.engine --postnet output/postnet_fp16.engine --waveglow output/waveglow_fp16.engine -o output/ --fp16 ``` + For TensorRT 8.0+, use `decoder_with_outer_loop_fp16.engine` for the decoder. + ## Performance ### Benchmarking diff --git a/demo/Tacotron2/common/utils.py b/demo/Tacotron2/common/utils.py index 3ed68cdf..27f8e311 100644 --- a/demo/Tacotron2/common/utils.py +++ b/demo/Tacotron2/common/utils.py @@ -19,6 +19,22 @@ from scipy.io.wavfile import read import torch import os +import argparse +import json + +class ParseFromConfigFile(argparse.Action): + + def __init__(self, option_strings, type, dest, help=None, required=False): + super(ParseFromConfigFile, self).__init__(option_strings=option_strings, type=type, dest=dest, help=help, required=required) + + def __call__(self, parser, namespace, values, option_string): + with open(values, 'r') as f: + data = json.load(f) + + for group in data.keys(): + for k,v in data[group].items(): + underscore_k = k.replace('-', '_') + setattr(namespace, underscore_k, v) def get_mask_from_lengths(lengths): max_len = torch.max(lengths).item() diff --git a/demo/Tacotron2/tensorrt/convert_onnx2trt.py b/demo/Tacotron2/tensorrt/convert_onnx2trt.py new file mode 100644 index 00000000..f742f4e8 --- /dev/null +++ b/demo/Tacotron2/tensorrt/convert_onnx2trt.py @@ -0,0 +1,168 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 +import pycuda.autoinit +import pycuda.driver as cuda +import sys +import tensorrt as trt +from os.path import join + +from trt_utils import build_engine, parse_dynamic_size + +def parse_args(parser): + """ + Parse commandline arguments. + """ + parser.add_argument('-o', '--output', required=True, + help='output folder to save audio (file per phrase)') + parser.add_argument('--encoder', type=str, default="", + help='full path to the Encoder ONNX') + parser.add_argument('--decoder', type=str, default="", + help='full path to the Decoder or DecoderIter ONNX.') + parser.add_argument('--postnet', type=str, default="", + help='full path to the Postnet ONNX') + parser.add_argument('--waveglow', type=str, default="", + help='full path to the WaveGlow ONNX') + parser.add_argument('--encoder_out', type=str, + help='Filename of the exported encoder engine') + parser.add_argument('--decoder_out', type=str, + help='Filename of the exported decoder engine') + parser.add_argument('--postnet_out', type=str, + help='Filename of the exported postnet engine') + parser.add_argument('--waveglow_out', type=str, + help='Filename of the exported waveglow engine') + parser.add_argument('--fp16', action='store_true', + help='inference with FP16') + parser.add_argument('-bs', '--batch-size', type=str, default="1", + help='One or three comma separated integers specifying the batch size. Specify "min,opt,max" for dynamic shape') + parser.add_argument('--mel-size', type=str, default="32,768,1664", + help='One or three comma separated integers specifying the mels size for waveglow.') + parser.add_argument('--z-size', type=str, default="1024,24576,53248", + help='One or three comma separated integers specifying the z size for waveglow.') + parser.add_argument('--loop', dest='loop', action='store_true', + help='Includes the outer decoder loop in the ONNX model. Enabled by default and only supported on TensorRT 8.0 or later.') + parser.add_argument('--no-loop', dest='loop', action='store_false', + help='Excludes outer decoder loop from decoder ONNX model. Default behavior and necessary for TensorRT 7.2 or earlier.') + parser.set_defaults(loop=int(trt.__version__[0]) >= 8) + + return parser + + +def main(): + + parser = argparse.ArgumentParser( + description='Export from ONNX to TensorRT for Tacotron 2 and WaveGlow') + parser = parse_args(parser) + args = parser.parse_args() + + precision = "fp16" if args.fp16 else "fp32" + encoder_path = join(args.output, args.encoder_out if args.encoder_out else f"encoder_{precision}.engine") + decoder_path = join(args.output, args.decoder_out if args.decoder_out else f"decoder_with_outer_loop_{precision}.engine" if args.loop else f"decoder_iter_{precision}.engine") + postnet_path = join(args.output, args.postnet_out if args.postnet_out else f"postnet_{precision}.engine") + waveglow_path = join(args.output, args.waveglow_out if args.waveglow_out else f"waveglow_{precision}.engine") + + bs_min, bs_opt, bs_max = parse_dynamic_size(args.batch_size) + mel_min, mel_opt, mel_max = parse_dynamic_size(args.mel_size) + z_min, z_opt, z_max = parse_dynamic_size(args.z_size) + + # Encoder + shapes=[{"name": "sequences", "min": (bs_min,4), "opt": (bs_opt,128), "max": (bs_max,256)}, + {"name": "sequence_lengths", "min": (bs_min,), "opt": (bs_opt,), "max": (bs_max,)}] + if args.encoder != "": + print("Building Encoder ...") + encoder_engine = build_engine(args.encoder, shapes=shapes, fp16=args.fp16) + if encoder_engine is not None: + with open(encoder_path, 'wb') as f: + f.write(encoder_engine.serialize()) + else: + print("Failed to build engine from", args.encoder) + sys.exit(1) + + if args.loop: + # Decoder + shapes=[{"name": "decoder_input_0", "min": (bs_min,80), "opt": (bs_opt,80), "max": (bs_max,80)}, + {"name": "attention_hidden_0", "min": (bs_min,1024), "opt": (bs_opt,1024), "max": (bs_max,1024)}, + {"name": "attention_cell_0", "min": (bs_min,1024), "opt": (bs_opt,1024), "max": (bs_max,1024)}, + {"name": "decoder_hidden_0", "min": (bs_min,1024), "opt": (bs_opt,1024), "max": (bs_max,1024)}, + {"name": "decoder_cell_0", "min": (bs_min,1024), "opt": (bs_opt,1024), "max": (bs_max,1024)}, + {"name": "attention_weights_0", "min": (bs_min,4), "opt": (bs_opt,128), "max": (bs_max,256)}, + {"name": "attention_weights_cum_0", "min": (bs_min,4), "opt": (bs_opt,128), "max": (bs_max,256)}, + {"name": "attention_context_0", "min": (bs_min,512), "opt": (bs_opt,512), "max": (bs_max,512)}, + {"name": "memory", "min": (bs_min,4,512), "opt": (bs_opt,128,512), "max": (bs_max,256,512)}, + {"name": "processed_memory", "min": (bs_min,4,128), "opt": (bs_opt,128,128), "max": (bs_max,256,128)}, + {"name": "mask", "min": (bs_min,4), "opt": (bs_opt,128), "max": (bs_max,256)}] + if args.decoder != "": + print("Building Decoder with loop...") + decoder_engine = build_engine(args.decoder, shapes=shapes, fp16=args.fp16) + if decoder_engine is not None: + with open(decoder_path, 'wb') as f: + f.write(decoder_engine.serialize()) + else: + print("Failed to build engine from", args.decoder) + sys.exit(1) + else: + # DecoderIter + shapes=[{"name": "decoder_input", "min": (bs_min,80), "opt": (bs_opt,80), "max": (bs_max,80)}, + {"name": "attention_hidden", "min": (bs_min,1024), "opt": (bs_opt,1024), "max": (bs_max,1024)}, + {"name": "attention_cell", "min": (bs_min,1024), "opt": (bs_opt,1024), "max": (bs_max,1024)}, + {"name": "decoder_hidden", "min": (bs_min,1024), "opt": (bs_opt,1024), "max": (bs_max,1024)}, + {"name": "decoder_cell", "min": (bs_min,1024), "opt": (bs_opt,1024), "max": (bs_max,1024)}, + {"name": "attention_weights", "min": (bs_min,4), "opt": (bs_opt,128), "max": (bs_max,256)}, + {"name": "attention_weights_cum", "min": (bs_min,4), "opt": (bs_opt,128), "max": (bs_max,256)}, + {"name": "attention_context", "min": (bs_min,512), "opt": (bs_opt,512), "max": (bs_max,512)}, + {"name": "memory", "min": (bs_min,4,512), "opt": (bs_opt,128,512), "max": (bs_max,256,512)}, + {"name": "processed_memory", "min": (bs_min,4,128), "opt": (bs_opt,128,128), "max": (bs_max,256,128)}, + {"name": "mask", "min": (bs_min,4), "opt": (bs_opt,128), "max": (bs_max,256)}] + if args.decoder != "": + print("Building Decoder ...") + decoder_iter_engine = build_engine(args.decoder, shapes=shapes, fp16=args.fp16) + if decoder_iter_engine is not None: + with open(decoder_path, 'wb') as f: + f.write(decoder_iter_engine.serialize()) + else: + print("Failed to build engine from", args.decoder) + sys.exit(1) + + # Postnet + shapes=[{"name": "mel_outputs", "min": (bs_min,80,32), "opt": (bs_opt,80,768), "max": (bs_max,80,1664)}] + if args.postnet != "": + print("Building Postnet ...") + postnet_engine = build_engine(args.postnet, shapes=shapes, fp16=args.fp16) + if postnet_engine is not None: + with open(postnet_path, 'wb') as f: + f.write(postnet_engine.serialize()) + else: + print("Failed to build engine from", args.postnet) + sys.exit(1) + + # WaveGlow + shapes=[{"name": "mel", "min": (bs_min,80,mel_min,1), "opt": (bs_opt,80,mel_opt,1), "max": (bs_max,80,mel_max,1)}, + {"name": "z", "min": (bs_min,8,z_min,1), "opt": (bs_opt,8,z_opt,1), "max": (bs_max,8,z_max,1)}] + if args.waveglow != "": + print("Building WaveGlow ...") + waveglow_engine = build_engine(args.waveglow, shapes=shapes, fp16=args.fp16) + if waveglow_engine is not None: + with open(waveglow_path, 'wb') as f: + f.write(waveglow_engine.serialize()) + else: + print("Failed to build engine from", args.waveglow) + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/demo/Tacotron2/exports/export_tacotron2_onnx.py b/demo/Tacotron2/tensorrt/convert_tacotron22onnx.py similarity index 86% rename from demo/Tacotron2/exports/export_tacotron2_onnx.py rename to demo/Tacotron2/tensorrt/convert_tacotron22onnx.py index 00f0bb21..9ca79392 100644 --- a/demo/Tacotron2/exports/export_tacotron2_onnx.py +++ b/demo/Tacotron2/tensorrt/convert_tacotron22onnx.py @@ -18,24 +18,39 @@ import torch from torch import nn from torch.nn import functional as F import argparse +import tensorrt import sys -sys.path.append('./') +import os +from pathlib import Path +sys.path.append(str(Path(__file__).parents[1])) import models from inference import checkpoint_from_distributed, unwrap_distributed, load_and_setup_model, prepare_input_sequence from common.utils import to_gpu, get_mask_from_lengths +torch.backends.cudnn.enabled = True def parse_args(parser): """ Parse commandline arguments. """ - parser.add_argument('--tacotron2', type=str, + parser.add_argument('--tacotron2', type=str, required=True, help='Full path to the Tacotron2 model checkpoint file') parser.add_argument('-o', '--output', type=str, required=True, - help='Directory for the exported Tacotron 2 ONNX model') + help='Directory for the exported Tacotron2 ONNX models') + parser.add_argument('-e', '--encoder', type=str, required=False, default="encoder.onnx", + help='Filename for exported encoder ONNX model') + parser.add_argument('-d', '--decoder', type=str, required=False, default="decoder_iter.onnx", + help='Filename for exported decoder ONNX model') + parser.add_argument('-p', '--postnet', type=str, required=False, default="postnet.onnx", + help='Filename for exported postnet ONNX model') parser.add_argument('--fp16', action='store_true', help='Export with half precision to ONNX') + parser.add_argument('--loop', dest='loop', action='store_true', + help='Includes the outer decoder loop in the ONNX model. Enabled by default and only supported on TensorRT 8.0 or later.') + parser.add_argument('--no-loop', dest='loop', action='store_false', + help='Excludes outer decoder loop from decoder ONNX model. Default behavior and necessary for TensorRT 7.2 or earlier.') + parser.set_defaults(loop=int(tensorrt.__version__[0]) >= 8) return parser @@ -211,7 +226,8 @@ def test_inference(encoder, decoder_iter, postnet): decoder_iter.eval() postnet.eval() - from trt.inference_trt import init_decoder_inputs + sys.path.append('./tensorrt') + from inference_trt import init_decoder_inputs texts = ["Hello World, good day."] sequences, sequence_lengths = prepare_input_sequence(texts) @@ -284,6 +300,10 @@ def main(): parser = parse_args(parser) args, _ = parser.parse_known_args() + args.encoder = os.path.join(args.output, args.encoder) + args.decoder = os.path.join(args.output, args.decoder) + args.postnet = os.path.join(args.output, args.postnet) + tacotron2 = load_and_setup_model('Tacotron2', parser, args.tacotron2, fp16_run=args.fp16, cpu_run=False) @@ -291,7 +311,7 @@ def main(): sequences = torch.randint(low=0, high=148, size=(1,50), dtype=torch.long).cuda() - sequence_lengths = torch.IntTensor([sequences.size(1)]).cuda().long() + sequence_lengths = torch.IntTensor([sequences.size(1)]) dummy_input = (sequences, sequence_lengths) encoder = Encoder(tacotron2) @@ -299,21 +319,23 @@ def main(): with torch.no_grad(): encoder(*dummy_input) - torch.onnx.export(encoder, dummy_input, args.output+"/"+"encoder.onnx", + torch.onnx.export(encoder, dummy_input, args.encoder, opset_version=opset_version, do_constant_folding=True, input_names=["sequences", "sequence_lengths"], output_names=["memory", "processed_memory", "lens"], dynamic_axes={"sequences": {0: "batch_size", 1: "text_seq"}, + "sequence_lengths": {0: "batch_size"}, "memory": {0: "batch_size", 1: "mem_seq"}, - "processed_memory": {0: "batch_size", 1: "mem_seq"} + "processed_memory": {0: "batch_size", 1: "mem_seq"}, + "lens": {0: "batch_size"} }) decoder_iter = DecoderIter(tacotron2) memory = torch.randn((1,sequence_lengths[0],512)).cuda() #encoder_outputs if args.fp16: memory = memory.half() - memory_lengths = sequence_lengths + memory_lengths = sequence_lengths.cuda() # initialize decoder states for dummy_input decoder_input = tacotron2.decoder.get_go_frame(memory) mask = get_mask_from_lengths(memory_lengths) @@ -342,7 +364,7 @@ def main(): with torch.no_grad(): decoder_iter(*dummy_input) - torch.onnx.export(decoder_iter, dummy_input, args.output+"/"+"decoder_iter.onnx", + torch.onnx.export(decoder_iter, dummy_input, args.decoder, opset_version=opset_version, do_constant_folding=True, input_names=["decoder_input", @@ -365,33 +387,25 @@ def main(): "out_attention_weights", "out_attention_weights_cum", "out_attention_context"], - dynamic_axes={"decoder_input" : {0: "batch_size"}, - "attention_hidden" : {0: "batch_size"}, - "attention_cell" : {0: "batch_size"}, - "decoder_hidden" : {0: "batch_size"}, - "decoder_cell" : {0: "batch_size"}, - "attention_weights" : {0: "batch_size", 1: "seq_len"}, + dynamic_axes={"attention_weights" : {0: "batch_size", 1: "seq_len"}, "attention_weights_cum" : {0: "batch_size", 1: "seq_len"}, - "attention_context" : {0: "batch_size"}, "memory" : {0: "batch_size", 1: "seq_len"}, "processed_memory" : {0: "batch_size", 1: "seq_len"}, "mask" : {0: "batch_size", 1: "seq_len"}, - "decoder_output" : {0: "batch_size"}, - "gate_prediction" : {0: "batch_size"}, - "out_attention_hidden" : {0: "batch_size"}, - "out_attention_cell" : {0: "batch_size"}, - "out_decoder_hidden" : {0: "batch_size"}, - "out_decoder_cell" : {0: "batch_size"}, "out_attention_weights" : {0: "batch_size", 1: "seq_len"}, - "out_attention_weights_cum" : {0: "batch_size", 1: "seq_len"}, - "out_attention_context" : {0: "batch_size"} + "out_attention_weights_cum" : {0: "batch_size", 1: "seq_len"} }) + if args.loop: + from generate_decoder import insert_decoder_loop + decoder_dir = os.path.dirname(os.path.abspath(args.decoder)) + insert_decoder_loop(args.decoder, decoder_dir, os.path.basename(args.decoder).replace("_iter", ""), args.fp16) + postnet = Postnet(tacotron2) dummy_input = torch.randn((1,80,620)).cuda() if args.fp16: dummy_input = dummy_input.half() - torch.onnx.export(postnet, dummy_input, args.output+"/"+"postnet.onnx", + torch.onnx.export(postnet, dummy_input, args.postnet, opset_version=opset_version, do_constant_folding=True, input_names=["mel_outputs"], @@ -399,8 +413,5 @@ def main(): dynamic_axes={"mel_outputs": {0: "batch_size", 2: "mel_seq"}, "mel_outputs_postnet": {0: "batch_size", 2: "mel_seq"}}) - mel = test_inference(encoder, decoder_iter, postnet) - torch.save(mel, "mel.pt") - if __name__ == '__main__': main() diff --git a/demo/Tacotron2/exports/export_waveglow_onnx.py b/demo/Tacotron2/tensorrt/convert_waveglow2onnx.py similarity index 68% rename from demo/Tacotron2/exports/export_waveglow_onnx.py rename to demo/Tacotron2/tensorrt/convert_waveglow2onnx.py index 3433514c..91d84f5b 100644 --- a/demo/Tacotron2/exports/export_waveglow_onnx.py +++ b/demo/Tacotron2/tensorrt/convert_waveglow2onnx.py @@ -14,30 +14,15 @@ # limitations under the License. # -import types import torch import argparse - +import os import sys -sys.path.append('./') - -from inference import checkpoint_from_distributed, unwrap_distributed, load_and_setup_model - -def parse_args(parser): - """ - Parse commandline arguments. - """ - parser.add_argument('--waveglow', type=str, required=True, - help='Full path to the WaveGlow model checkpoint file') - parser.add_argument('-o', '--output', type=str, required=True, - help='Directory for the exported WaveGlow ONNX model') - parser.add_argument('--fp16', action='store_true', - help='Inference with Automatic Mixed Precision') - parser.add_argument('-s', '--sigma-infer', default=0.6, type=float, - help='Standard deviation of the Gaussian distribution') - - return parser +from pathlib import Path +sys.path.append(str(Path(__file__).parents[1])) +from common.utils import ParseFromConfigFile +from inference import load_and_setup_model def convert_convinv_1d_to_2d(convinv): """ @@ -105,52 +90,22 @@ def convert_1d_to_2d_(glow): glow.cuda() +def parse_args(parser): + """ + Parse commandline arguments. + """ + parser.add_argument('--waveglow', type=str, required=True, + help='full path to the WaveGlow model checkpoint file') + parser.add_argument('-o', '--output', type=str, required=True, + help='Directory or file name for the exported WaveGlow ONNX model') + parser.add_argument('--fp16', action='store_true', + help='inference with AMP') + parser.add_argument('-s', '--sigma-infer', default=0.6, type=float) -def infer_onnx(self, spect, z, sigma=0.9): + parser.add_argument('--config-file', action=ParseFromConfigFile, + type=str, help='Path to configuration file') - spect = self.upsample(spect) - # trim conv artifacts. maybe pad spec to kernel multiple - time_cutoff = self.upsample.kernel_size[0] - self.upsample.stride[0] - spect = spect[:, :, :-time_cutoff] - - length_spect_group = spect.size(2)//8 - mel_dim = 80 - batch_size = spect.size(0) - - spect = torch.squeeze(spect, 3) - spect = spect.view((batch_size, mel_dim, length_spect_group, self.n_group)) - spect = spect.permute(0, 2, 1, 3) - spect = spect.contiguous() - spect = spect.view((batch_size, length_spect_group, self.n_group*mel_dim)) - spect = spect.permute(0, 2, 1) - spect = torch.unsqueeze(spect, 3) - spect = spect.contiguous() - - audio = z[:, :self.n_remaining_channels, :, :] - z = z[:, self.n_remaining_channels:self.n_group, :, :] - audio = sigma*audio - - for k in reversed(range(self.n_flows)): - n_half = int(audio.size(1) / 2) - audio_0 = audio[:, :n_half, :, :] - audio_1 = audio[:, n_half:(n_half+n_half), :, :] - - output = self.WN[k]((audio_0, spect)) - s = output[:, n_half:(n_half+n_half), :, :] - b = output[:, :n_half, :, :] - audio_1 = (audio_1 - b) / torch.exp(s) - audio = torch.cat([audio_0, audio_1], 1) - - audio = self.convinv[k](audio) - - if k % self.n_early_every == 0 and k > 0: - audio = torch.cat((z[:, :self.n_early_size, :, :], audio), 1) - z = z[:, self.n_early_size:self.n_group, :, :] - - audio = torch.squeeze(audio, 3) - audio = audio.permute(0,2,1).contiguous().view(batch_size, (length_spect_group * self.n_group)) - - return audio + return parser def export_onnx(parser, args): @@ -173,18 +128,23 @@ def export_onnx(parser, args): # run inference to force calculation of inverses waveglow.infer(mel, sigma=args.sigma_infer) - # export to ONNX convert_1d_to_2d_(waveglow) + mel = mel.unsqueeze(3) + + # export to ONNX if args.fp16: waveglow = waveglow.half() - fType = types.MethodType - waveglow.forward = fType(infer_onnx, waveglow) + waveglow.forward = waveglow.infer_onnx - mel = mel.unsqueeze(3) - opset_version = 10 + opset_version = 11 - torch.onnx.export(waveglow, (mel, z), args.output+"/"+"waveglow.onnx", + if os.path.isdir(args.output): + output_path = os.path.join(args.output, "waveglow.onnx") + else: + output_path = args.output + + torch.onnx.export(waveglow, (mel, z), output_path, opset_version=opset_version, do_constant_folding=True, input_names=["mel", "z"], @@ -195,7 +155,6 @@ def export_onnx(parser, args): def main(): - parser = argparse.ArgumentParser( description='PyTorch Tacotron 2 Inference') parser = parse_args(parser) diff --git a/demo/Tacotron2/tensorrt/generate_decoder.py b/demo/Tacotron2/tensorrt/generate_decoder.py new file mode 100644 index 00000000..7e7a25b0 --- /dev/null +++ b/demo/Tacotron2/tensorrt/generate_decoder.py @@ -0,0 +1,211 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 onnx +import sys +import os +import numpy as np +import argparse + +def insert_decoder_loop(decoder_iter_onnx_path, output_dir, decoder_out_name, fp16): + float_prec = np.float16 if fp16 else np.float32 + + # Modify loop body so that it has 2+N inputs: (iteration_num, condition, loop carried dependencies...) + # and 1+N+K outputs: (condition, loop carried dependencies..., scan_outputs...) + + # In this case, the loop carried dependencies include the following IN ORDER + # - decoder_output/decoder_input + # - attention_hidden + # - attention_cell + # - decoder_hidden + # - decoder_cell + # - attention_weights + # - attention_weights_cum + # - attention_context + # - not_finished (bool tensor, initialized to all True) + # - mel_lengths + + # The following are NOT loop carried dependencies (they remain constant through the loop), and must be moved to be inputs outside of the loop body + # - memory + # - processed_memory + # - mask + + # The scan outputs are + # - mel_outputs (which scans across decoder_output) + # - gate_outputs (scans across gate_prediction) + # - alignments (scans across attention_weights) + + + loop_body = gs.import_onnx(onnx.load(decoder_iter_onnx_path)) + loop_tensors = loop_body.tensors() + + iteration_num = gs.Variable("iteration_num", dtype=np.int64, shape=()) + cond_in = gs.Variable("cond_in", dtype=bool, shape=()) + cond_out = gs.Variable("cond_out", dtype=bool, shape=()) + not_finished_in = gs.Variable("not_finished_in", shape=('batch_size', 1), dtype=bool) + not_finished_out = gs.Variable("not_finished_out", shape=('batch_size', 1), dtype=bool) + mel_lengths_in = gs.Variable("mel_lengths_in", shape=('batch_size', 1), dtype=np.int32) + mel_lengths_out = gs.Variable("mel_lengths_out", shape=('batch_size', 1), dtype=np.int32) + + + # Set loop body inputs in the correct order + loop_body.inputs = [iteration_num, cond_in, loop_tensors["decoder_input"], loop_tensors["attention_hidden"], loop_tensors["attention_cell"], loop_tensors["decoder_hidden"], loop_tensors["decoder_cell"], loop_tensors["attention_weights"], loop_tensors["attention_weights_cum"], loop_tensors["attention_context"], not_finished_in, mel_lengths_in] + + # Set loop body outputs in the correct order + loop_body.outputs = [cond_out, loop_tensors["decoder_output"], loop_tensors["out_attention_hidden"], loop_tensors["out_attention_cell"], loop_tensors["out_decoder_hidden"], loop_tensors["out_decoder_cell"], loop_tensors["out_attention_weights"], loop_tensors["out_attention_weights_cum"], loop_tensors["out_attention_context"], not_finished_out, mel_lengths_out, loop_tensors["decoder_output"], loop_tensors["gate_prediction"], loop_tensors["out_attention_weights"]] + + # The loop stop condition is given by the following lines in PyTorch + # dec = torch.le(torch.sigmoid(decoder_outputs[8]), gate_threshold).to(torch.int32).squeeze(1) + # not_finished = not_finished*dec + # if torch.sum(not_finished) == 0: + # break + + # To compute cond_out, we can essentially follow the same steps. Using Less instead of Greater+Not for now + + gate_threshold = gs.Constant("gate_threshold", np.array([0.5], dtype=float_prec)) + gate_sigmoid = gs.Variable("gate_sigmoid", dtype=float_prec, shape=()) + sigmoid = loop_body.nodes.append(gs.Node(op="Sigmoid", inputs=[loop_tensors["gate_prediction"]], outputs=[gate_sigmoid])) + + leq_output = gs.Variable("leq_output", dtype=bool) + leq = loop_body.nodes.append(gs.Node(op="Less", inputs=[gate_sigmoid, gate_threshold], outputs=[leq_output])) + + loop_body.nodes.append(gs.Node(op="And", inputs=[not_finished_in, leq_output], outputs=[not_finished_out])) + + cast_output = gs.Variable("cast_output", dtype=np.int32) + loop_body.nodes.append(gs.Node(op="Cast", inputs=[not_finished_out], outputs=[cast_output], attrs={"to": 6})) # int32 + + reduce_output = gs.Variable("reduce_output", dtype=np.int32) + loop_body.nodes.append( gs.Node(op="ReduceSum", inputs=[cast_output], outputs=[reduce_output], attrs={"axes": [0], "keepdims": 0})) + + unsqueezed_cond_out = gs.Variable("unsqueezed_cond_out", dtype=bool) + loop_body.nodes.append(gs.Node(op="Equal", inputs=[reduce_output, gs.Constant("zero", np.array(0, dtype=np.int32))], outputs=[unsqueezed_cond_out])) + + squeezed_cond_out = gs.Variable("squeezed_cond_out", dtype=bool) + loop_body.nodes.append(gs.Node(op="Squeeze", inputs=[unsqueezed_cond_out], outputs=[squeezed_cond_out], attrs={"axes": [0]})) + + loop_body.nodes.append(gs.Node(op="Not", inputs=[squeezed_cond_out], outputs=[cond_out])) + + # Compute mel_lengths + # from PyTorch: mel_lengths += not_finished + + loop_body.nodes.append(gs.Node(op="Add", inputs=[mel_lengths_in, cast_output], outputs=[mel_lengths_out])) + + memory = gs.Variable("memory", dtype=float_prec, shape=('batch_size', 'seq_len', 512)) + processed_memory = gs.Variable("processed_memory", dtype=float_prec, shape=('batch_size', 'seq_len', 128)) + mask = gs.Variable("mask", dtype=bool, shape=('batch_size', 'seq_len')) + + loop_body.toposort() + onnx.save(gs.export_onnx(loop_body), os.path.join(output_dir, "loop_body_{prec}.onnx".format(prec="fp16" if float_prec == np.float16 else "fp32"))) + + # Create outer graph + + # Inputs to outer graph are the following (suffixed with _0 to signify initial states) + # - decoder_input_0 + # - attention_hidden_0 + # - attention_cell_0 + # - decoder_hidden_0 + # - decoder_cell_0 + # - attention_weights_0 + # - attention_weights_cum_0 + # - attention_context_0 + # - memory + # - processed_memory + # - mask + + # Outputs are the following + # - mel_outputs + # - mel_lengths + + # Note: alignments and gate_outputs are scan outputs, but don't seem to be used later in the PyTorch implementation. For now, we will make them intermediate tensors that are not outputted + + graph = gs.Graph() + + decoder_input_0 = gs.Variable("decoder_input_0", dtype=float_prec, shape=('batch_size', 80)) + attention_hidden_0 = gs.Variable("attention_hidden_0", dtype=float_prec, shape=('batch_size', 1024)) + attention_cell_0 = gs.Variable("attention_cell_0", dtype=float_prec, shape=('batch_size', 1024)) + decoder_hidden_0 = gs.Variable("decoder_hidden_0", dtype=float_prec, shape=('batch_size', 1024)) + decoder_cell_0 = gs.Variable("decoder_cell_0", dtype=float_prec, shape=('batch_size', 1024)) + attention_weights_0 = gs.Variable("attention_weights_0", dtype=float_prec, shape=('batch_size', 'seq_len')) + attention_weights_cum_0 = gs.Variable("attention_weights_cum_0", dtype=float_prec, shape=('batch_size', 'seq_len')) + attention_context_0 = gs.Variable("attention_context_0", dtype=float_prec, shape=('batch_size', 512)) + not_finished_0 = gs.Variable("not_finished_0", dtype=bool) + mel_lengths_0 = gs.Variable("mel_lengths_0", dtype=np.int32) + + # For not_finished, we need to generate a tensor of shape (batch_size) that is all 1s + # We can use the ONNX ConstantOfShape op to do this + not_finished_shape = gs.Variable("not_finished_shape", dtype=np.int64) + reduced = gs.Variable("reduced", dtype=float_prec) + graph.nodes.append(gs.Node(op="ReduceSum", inputs=[decoder_input_0], outputs=[reduced], attrs={"axes":[1], "keepdims": 1})) + graph.nodes.append(gs.Node(op="Shape", inputs=[reduced], outputs=[not_finished_shape])) + before_cast = gs.Variable("before_cast", dtype=np.int32) + graph.nodes.append(gs.Node(op="ConstantOfShape", inputs=[not_finished_shape], outputs=[before_cast], attrs={"value":gs.Constant("one", np.array([1], dtype=np.int32))})) + graph.nodes.append(gs.Node(op="Cast", inputs=[before_cast], outputs=[not_finished_0], attrs={"to": 9})) + + # Same thing for mel_lengths, but we need all 0s + graph.nodes.append(gs.Node(op="ConstantOfShape", inputs=[not_finished_shape], outputs=[mel_lengths_0], attrs={"value":gs.Constant("zero", np.array([0], dtype=np.int32))})) + + # Loop carried dependecies at the end of the loop + decoder_input_t = gs.Variable("decoder_input_t", dtype=float_prec, shape=('batch_size', 80)) + attention_hidden_t = gs.Variable("attention_hidden_t", dtype=float_prec, shape=('batch_size', 1024)) + attention_cell_t = gs.Variable("attention_cell_t", dtype=float_prec, shape=('batch_size', 1024)) + decoder_hidden_t = gs.Variable("decoder_hidden_t", dtype=float_prec, shape=('batch_size', 1024)) + decoder_cell_t = gs.Variable("decoder_cell_t", dtype=float_prec, shape=('batch_size', 1024)) + attention_weights_t = gs.Variable("attention_weights_t", dtype=float_prec, shape=('batch_size', 'seq_len')) + attention_weights_cum_t = gs.Variable("attention_weights_cum_t", dtype=float_prec, shape=('batch_size', 'seq_len')) + attention_context_t = gs.Variable("attention_context_t", dtype=float_prec, shape=('batch_size', 512)) + not_finished_t = gs.Variable("not_finished_t", dtype=bool) + mel_lengths_t = gs.Variable("mel_lengths_t", dtype=np.int32, shape=('batch_size', 1)) + + # Scan outputs + mel_outputs_raw = gs.Variable("mel_outputs_raw", dtype=float_prec, shape=(-1, 'batch_size', 80)) + gate_outputs = gs.Variable("gate_outputs", dtype=float_prec, shape=(-1, 'batch_size', 1)) + alignments = gs.Variable("alignments", dtype=float_prec, shape=(-1, 1, 'seq_len')) + + mel_outputs = gs.Variable("mel_outputs", dtype=float_prec, shape=('batch_size', 80, -1)) + + graph.inputs = [decoder_input_0, attention_hidden_0, attention_cell_0, decoder_hidden_0, decoder_cell_0, attention_weights_0, attention_weights_cum_0, attention_context_0, memory, processed_memory, mask] + graph.outputs = [mel_outputs, mel_lengths_t] + + trip_count = gs.Constant("trip_count", np.array(0, dtype=np.int64)) # In ONNX, this is an optional parameter, but I don't think ONNX-GS supports optional inputs. To fix this, after we export the ONNX ModelProto from GS, we replace this input with "" + initial_cond = gs.Constant("initial_cond", np.array(True, dtype=bool)) + loop_inputs = [trip_count, initial_cond, decoder_input_0, attention_hidden_0, attention_cell_0, decoder_hidden_0, decoder_cell_0, attention_weights_0, attention_weights_cum_0, attention_context_0, not_finished_0, mel_lengths_0] + loop_outputs = [decoder_input_t, attention_hidden_t, attention_cell_t, decoder_hidden_t, decoder_cell_t, attention_weights_t, attention_weights_cum_t, attention_context_t, not_finished_t, mel_lengths_t, mel_outputs_raw, gate_outputs, alignments] + decoder_loop = gs.Node(op="Loop", name="decoder_loop", inputs=loop_inputs, outputs=loop_outputs, attrs={"body": loop_body}) + graph.nodes.append(decoder_loop) + + graph.nodes.append(gs.Node(op="Transpose", inputs=[mel_outputs_raw], outputs=[mel_outputs], attrs={"perm": [1, 2, 0]})) # Output needs to have loop dimension as inner-most dim + + graph.toposort() + exported_graph = gs.export_onnx(graph) + [x for x in exported_graph.graph.node if x.name == "decoder_loop"][0].input[0] = "" # Remove trip count input + + onnx.save(exported_graph, os.path.join(output_dir, decoder_out_name)) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('model_path', type=str, + help='path to original decoder_iter ONNX model') + parser.add_argument('-o', '--output_dir', type=str, default='.', help='Output directory') + parser.add_argument('--decoder_out', type=str, help='Filename of the exported decoder with outer loop') + parser.add_argument('--fp16', action='store_true') + + args = parser.parse_args() + + if args.decoder_out == None: + args.decoder_out = "decoder_with_outer_loop_{}.onnx".format("fp16" if args.fp16 else "fp32") + + insert_decoder_loop(args.model_path, args.output_dir, args.decoder_out, args.fp16) \ No newline at end of file diff --git a/demo/Tacotron2/trt/inference_trt.py b/demo/Tacotron2/tensorrt/inference_trt.py similarity index 59% rename from demo/Tacotron2/trt/inference_trt.py rename to demo/Tacotron2/tensorrt/inference_trt.py index c05dbfcc..04200664 100644 --- a/demo/Tacotron2/trt/inference_trt.py +++ b/demo/Tacotron2/tensorrt/inference_trt.py @@ -20,16 +20,18 @@ from scipy.io.wavfile import write import time import torch import argparse -import sys +import os.path as path -sys.path.append('./') +import sys +from pathlib import Path +sys.path.append(str(Path(__file__).parents[1])) from common.utils import to_gpu, get_mask_from_lengths from tacotron2.text import text_to_sequence from inference import MeasureTime, prepare_input_sequence, load_and_setup_model import dllogger as DLLogger from dllogger import StdOutBackend, JSONStreamBackend, Verbosity -from trt.trt_utils import load_engine, run_trt_engine +from trt_utils import load_engine, run_trt_engine from waveglow.denoiser import Denoiser @@ -38,30 +40,37 @@ def parse_args(parser): Parse commandline arguments. """ parser.add_argument('-i', '--input', type=str, required=True, - help='Full path to the input text (phareses separated by new line)') + help='full path to the input text (phareses separated by new line)') parser.add_argument('-o', '--output', required=True, - help='Output folder to save audio (file per phrase)') + help='output folder to save audio (file per phrase)') parser.add_argument('--encoder', type=str, required=True, - help='Full path to the Encoder engine') + help='full path to the Encoder engine') parser.add_argument('--decoder', type=str, required=True, - help='Full path to the DecoderIter engine') + help='full path to the DecoderIter engine') parser.add_argument('--postnet', type=str, required=True, - help='Full path to the Postnet engine') + help='full path to the Postnet engine') parser.add_argument('--waveglow', type=str, required=True, - help='Full path to the WaveGlow engine') + help='full path to the WaveGlow engine') parser.add_argument('--waveglow-ckpt', type=str, default="", - help='Full path to the WaveGlow model checkpoint file') + help='full path to the WaveGlow model checkpoint file') parser.add_argument('--log-file', type=str, default='nvlog.json', help='Filename for logging') - parser.add_argument('-d', '--denoising-strength', default=0.01, type=float, - help='Denoising strength for removing model bias') + parser.add_argument('-d', '--denoising-strength', default=0.01, type=float) parser.add_argument('-sr', '--sampling-rate', default=22050, type=int, help='Sampling rate') parser.add_argument('--stft-hop-length', type=int, default=256, help='STFT hop length for estimating audio length from mel size') parser.add_argument('--fp16', action='store_true', - help='Inference with FP16 precision') - + help='inference with FP16') + parser.add_argument('--loop', dest='loop', action='store_true', + help='Includes the outer decoder loop in the ONNX model. Enabled by default and only supported on TensorRT 8.0 or later.') + parser.add_argument('--no-loop', dest='loop', action='store_false', + help='Excludes outer decoder loop from decoder ONNX model. Default behavior and necessary for TensorRT 7.2 or earlier.') + parser.set_defaults(loop=int(trt.__version__[0]) >= 8) + parser.add_argument('--waveglow-onnxruntime', action='store_true', + help='Specify this option to use ONNX runtime instead of TRT for running Waveglow') + parser.add_argument('--decoder-onnxruntime', action='store_true', + help='Specify this option to use ONNX runtime instead of TRT for running the TT2 Decoder with loop. When using this option, pass the decoder ONNX model to the --decoder argument') return parser @@ -174,16 +183,19 @@ def swap_inputs_outputs(decoder_inputs, decoder_outputs): def infer_tacotron2_trt(encoder, decoder_iter, postnet, encoder_context, decoder_context, postnet_context, - sequences, sequence_lengths, measurements, fp16): + sequences, sequence_lengths, measurements, fp16, loop): - memory = torch.zeros((len(sequence_lengths), sequence_lengths[0], 512)).cuda() + batch_size = len(sequence_lengths) + max_sequence_len = sequence_lengths[0] + memory = torch.zeros((batch_size, max_sequence_len, 512)).cuda() if fp16: memory = memory.half() device = memory.device dtype = memory.dtype - processed_memory = torch.zeros((len(sequence_lengths),sequence_lengths[0],128), device=device, dtype=dtype) + processed_memory = torch.zeros((batch_size, max_sequence_len, 128), device=device, dtype=dtype) lens = torch.zeros_like(sequence_lengths) + print(f"batch_size: {batch_size}, max sequence length: {max_sequence_len}") encoder_tensors = { "inputs" : @@ -195,50 +207,101 @@ def infer_tacotron2_trt(encoder, decoder_iter, postnet, print("Running Tacotron2 Encoder") with MeasureTime(measurements, "tacotron2_encoder_time"): run_trt_engine(encoder_context, encoder, encoder_tensors) - + max_decoder_steps = 1024 device = memory.device mel_lengths = torch.zeros([memory.size(0)], dtype=torch.int32, device = device) not_finished = torch.ones([memory.size(0)], dtype=torch.int32, device = device) - mel_outputs, gate_outputs, alignments = (torch.zeros(1, device = device), torch.zeros(1, device = device), torch.zeros(1, device = device)) + mel_outputs = torch.ones((batch_size, 80, max_decoder_steps), device = device, dtype=dtype).cuda() gate_threshold = 0.5 - max_decoder_steps = 1664 first_iter = True decoder_inputs = init_decoder_inputs(memory, processed_memory, sequence_lengths) decoder_outputs = init_decoder_outputs(memory, sequence_lengths) - print("Running Tacotron2 Decoder") - measurements_decoder = {} - while True: - decoder_tensors = init_decoder_tensors(decoder_inputs, decoder_outputs) - with MeasureTime(measurements_decoder, "step"): - run_trt_engine(decoder_context, decoder_iter, decoder_tensors) + if loop: + if decoder_context is None: + print("Running Tacotron2 Decoder with loop with ONNX-RT") + decoder_inputs_onnxrt = [x.cpu().numpy().copy() for x in decoder_inputs] + import onnx + import onnxruntime + sess = onnxruntime.InferenceSession(decoder_iter) - if first_iter: - mel_outputs = torch.unsqueeze(decoder_outputs[7], 2) - gate_outputs = torch.unsqueeze(decoder_outputs[8], 2) - alignments = torch.unsqueeze(decoder_outputs[4], 2) - measurements['tacotron2_decoder_time'] = measurements_decoder['step'] - first_iter = False - else: - mel_outputs = torch.cat((mel_outputs, torch.unsqueeze(decoder_outputs[7], 2)), 2) - gate_outputs = torch.cat((gate_outputs, torch.unsqueeze(decoder_outputs[8], 2)), 2) - alignments = torch.cat((alignments, torch.unsqueeze(decoder_outputs[4], 2)), 2) - measurements['tacotron2_decoder_time'] += measurements_decoder['step'] + with MeasureTime(measurements, "tacotron2_decoder_time"): + result = sess.run(["mel_outputs", "mel_lengths_t"], { + 'decoder_input_0': decoder_inputs_onnxrt[0], + 'attention_hidden_0': decoder_inputs_onnxrt[1], + 'attention_cell_0': decoder_inputs_onnxrt[2], + 'decoder_hidden_0': decoder_inputs_onnxrt[3], + 'decoder_cell_0': decoder_inputs_onnxrt[4], + 'attention_weights_0': decoder_inputs_onnxrt[5], + 'attention_weights_cum_0': decoder_inputs_onnxrt[6], + 'attention_context_0': decoder_inputs_onnxrt[7], + 'memory': decoder_inputs_onnxrt[8], + 'processed_memory': decoder_inputs_onnxrt[9], + 'mask': decoder_inputs_onnxrt[10] + }) - dec = torch.le(torch.sigmoid(decoder_outputs[8]), gate_threshold).to(torch.int32).squeeze(1) - not_finished = not_finished*dec - mel_lengths += not_finished + mel_outputs = torch.tensor(result[0], device=device) + mel_lengths = torch.tensor(result[1], device=device) + else: + print("Running Tacotron2 Decoder with loop") + decoder_tensors = { + "inputs" : + { + 'decoder_input_0': decoder_inputs[0], + 'attention_hidden_0': decoder_inputs[1], + 'attention_cell_0': decoder_inputs[2], + 'decoder_hidden_0': decoder_inputs[3], + 'decoder_cell_0': decoder_inputs[4], + 'attention_weights_0': decoder_inputs[5], + 'attention_weights_cum_0': decoder_inputs[6], + 'attention_context_0': decoder_inputs[7], + 'memory': decoder_inputs[8], + 'processed_memory': decoder_inputs[9], + 'mask': decoder_inputs[10] + }, + "outputs" : + {'mel_outputs': mel_outputs, 'mel_lengths_t': mel_lengths} + } - if torch.sum(not_finished) == 0: - print("Stopping after",mel_outputs.size(2),"decoder steps") - break - if mel_outputs.size(2) == max_decoder_steps: - print("Warning! Reached max decoder steps") - break + with MeasureTime(measurements, "tacotron2_decoder_time"): + run_trt_engine(decoder_context, decoder_iter, decoder_tensors) + mel_outputs = mel_outputs[:,:,:torch.max(mel_lengths)] - decoder_inputs, decoder_outputs = swap_inputs_outputs(decoder_inputs, decoder_outputs) + else: + print("Running Tacotron2 Decoder") + measurements_decoder = {} + while True: + decoder_tensors = init_decoder_tensors(decoder_inputs, decoder_outputs) + with MeasureTime(measurements_decoder, "step"): + run_trt_engine(decoder_context, decoder_iter, decoder_tensors) + if first_iter: + mel_outputs = torch.unsqueeze(decoder_outputs[7], 2) + gate_outputs = torch.unsqueeze(decoder_outputs[8], 2) + alignments = torch.unsqueeze(decoder_outputs[4], 2) + measurements['tacotron2_decoder_time'] = measurements_decoder['step'] + first_iter = False + else: + mel_outputs = torch.cat((mel_outputs, torch.unsqueeze(decoder_outputs[7], 2)), 2) + gate_outputs = torch.cat((gate_outputs, torch.unsqueeze(decoder_outputs[8], 2)), 2) + alignments = torch.cat((alignments, torch.unsqueeze(decoder_outputs[4], 2)), 2) + measurements['tacotron2_decoder_time'] += measurements_decoder['step'] + + dec = torch.le(torch.sigmoid(decoder_outputs[8]), gate_threshold).to(torch.int32).squeeze(1) + not_finished = not_finished*dec + mel_lengths += not_finished + + if torch.sum(not_finished) == 0: + print("Stopping after",mel_outputs.size(2),"decoder steps") + break + if mel_outputs.size(2) == max_decoder_steps: + print("Warning! Reached max decoder steps") + break + + decoder_inputs, decoder_outputs = swap_inputs_outputs(decoder_inputs, decoder_outputs) + + mel_outputs = mel_outputs.clone().detach() mel_outputs_postnet = torch.zeros_like(mel_outputs, device=device, dtype=dtype) postnet_tensors = { @@ -255,6 +318,7 @@ def infer_tacotron2_trt(encoder, decoder_iter, postnet, return mel_outputs_postnet, mel_lengths + def infer_waveglow_trt(waveglow, waveglow_context, mel, measurements, fp16): mel_size = mel.size(2) @@ -264,9 +328,10 @@ def infer_waveglow_trt(waveglow, waveglow_context, mel, measurements, fp16): z_size = mel_size*stride z_size = z_size//n_group z = torch.randn(batch_size, n_group, z_size).cuda() + audios = torch.zeros(batch_size, mel_size*stride).cuda() + mel = mel.unsqueeze(3) z = z.unsqueeze(3) - audios = torch.zeros(batch_size, mel_size*stride).cuda() if fp16: z = z.half() @@ -274,17 +339,49 @@ def infer_waveglow_trt(waveglow, waveglow_context, mel, measurements, fp16): audios = audios.half() waveglow_tensors = { - "inputs" : - {'mel': mel, 'z': z}, - "outputs" : - {'audio': audios} + "inputs" : {'mel': mel, 'z': z}, + "outputs" : {'audio': audios} } - print("Running WaveGlow") + + print("Running WaveGlow with TensorRT") with MeasureTime(measurements, "waveglow_time"): run_trt_engine(waveglow_context, waveglow, waveglow_tensors) return audios +def infer_waveglow_onnx(waveglow_path, mel, measurements, fp16): + import onnx + import onnxruntime + sess = onnxruntime.InferenceSession(waveglow_path) + + device=mel.device + mel_size = mel.size(2) + batch_size = mel.size(0) + stride = 256 + n_group = 8 + z_size = mel_size*stride + z_size = z_size//n_group + z = torch.randn(batch_size, n_group, z_size).cuda() + + mel = mel.unsqueeze(3) + z = z.unsqueeze(3) + + if fp16: + z = z.half() + mel = mel.half() + + mel = mel.cpu().numpy().copy() + z = z.cpu().numpy().copy() + + print("Running WaveGlow with ONNX Runtime") + with MeasureTime(measurements, "waveglow_time"): + result = sess.run(["audio"], { + 'mel': mel, + 'z': z + }) + audios = torch.tensor(result[0], device=device) + return audios + def main(): parser = argparse.ArgumentParser( @@ -297,27 +394,35 @@ def main(): TRT_LOGGER = trt.Logger(trt.Logger.WARNING) encoder = load_engine(args.encoder, TRT_LOGGER) - decoder_iter = load_engine(args.decoder, TRT_LOGGER) postnet = load_engine(args.postnet, TRT_LOGGER) - waveglow = load_engine(args.waveglow, TRT_LOGGER) if args.waveglow_ckpt != "": # setup denoiser using WaveGlow PyTorch checkpoint waveglow_ckpt = load_and_setup_model('WaveGlow', parser, args.waveglow_ckpt, True, forward_is_infer=True) denoiser = Denoiser(waveglow_ckpt).cuda() - # after initialization, we don't need WaveGlow PyTorch checkpoint anymore - deleting + # after initialization, we don't need WaveGlow PyTorch checkpoint + # anymore - deleting del waveglow_ckpt torch.cuda.empty_cache() # create TRT contexts for each engine encoder_context = encoder.create_execution_context() - decoder_context = decoder_iter.create_execution_context() + decoder_context = None + if not args.decoder_onnxruntime: + decoder_iter = load_engine(args.decoder, TRT_LOGGER) + decoder_context = decoder_iter.create_execution_context() + else: + decoder_iter = args.decoder postnet_context = postnet.create_execution_context() - waveglow_context = waveglow.create_execution_context() + + waveglow_context = None + if not args.waveglow_onnxruntime: + waveglow = load_engine(args.waveglow, TRT_LOGGER) + waveglow_context = waveglow.create_execution_context() DLLogger.init(backends=[JSONStreamBackend(Verbosity.DEFAULT, - args.output+'/'+args.log_file), + path.join(args.output, args.log_file)), StdOutBackend(Verbosity.VERBOSE)]) texts = [] @@ -333,14 +438,22 @@ def main(): sequences, sequence_lengths = prepare_input_sequence(texts) sequences = sequences.to(torch.int32) sequence_lengths = sequence_lengths.to(torch.int32) + with MeasureTime(measurements, "latency"): mel, mel_lengths = infer_tacotron2_trt(encoder, decoder_iter, postnet, encoder_context, decoder_context, postnet_context, - sequences, sequence_lengths, measurements, args.fp16) - audios = infer_waveglow_trt(waveglow, waveglow_context, mel, measurements, args.fp16) + sequences, sequence_lengths, measurements, args.fp16, args.loop) + audios = infer_waveglow_onnx(args.waveglow, mel, measurements, args.fp16) if args.waveglow_onnxruntime else \ + infer_waveglow_trt(waveglow, waveglow_context, mel, measurements, args.fp16) - with encoder_context, decoder_context, postnet_context, waveglow_context: + with encoder_context, postnet_context: pass + + if decoder_context is not None: + with decoder_context: pass + + if waveglow_context is not None: + with waveglow_context: pass audios = audios.float() if args.waveglow_ckpt != "": @@ -350,7 +463,7 @@ def main(): for i, audio in enumerate(audios): audio = audio[:mel_lengths[i]*args.stft_hop_length] audio = audio/torch.max(torch.abs(audio)) - audio_path = args.output + "audio_"+str(i)+"_trt.wav" + audio_path = path.join(args.output, f"audio_{i}_trt.wav") write(audio_path, args.sampling_rate, audio.cpu().numpy()) @@ -367,8 +480,9 @@ def main(): prec = "fp16" if args.fp16 else "fp32" latency = measurements['latency'] throughput = audios.size(1)/latency - log_data = "1,"+str(sequence_lengths[0].item())+","+prec+","+str(latency)+","+str(throughput)+","+str(mel_lengths[0].item())+"\n" - with open("log_bs1_"+prec+".log", 'a') as f: + log_data = f"1,{sequence_lengths[0].item()},{prec},{latency},{throughput},{mel_lengths[0].item()}\n" + log_file = path.join(args.output, f"log_bs1_{prec}.log") + with open(log_file, 'a') as f: f.write(log_data) if __name__ == "__main__": diff --git a/demo/Tacotron2/trt/run_latency_tests_trt.sh b/demo/Tacotron2/tensorrt/run_latency_tests_trt.sh similarity index 68% rename from demo/Tacotron2/trt/run_latency_tests_trt.sh rename to demo/Tacotron2/tensorrt/run_latency_tests_trt.sh index 2040d804..07dfd704 100644 --- a/demo/Tacotron2/trt/run_latency_tests_trt.sh +++ b/demo/Tacotron2/tensorrt/run_latency_tests_trt.sh @@ -14,4 +14,4 @@ # limitations under the License. # -bash test_infer.sh --test trt/test_infer_trt.py -bs 1 -il 128 --fp16 --num-iters 1003 --encoder ./output/encoder_fp16.engine --decoder ./output/decoder_iter_fp16.engine --postnet ./output/postnet_fp16.engine --waveglow ./output/waveglow_fp16.engine --wn-channels 256 +bash test_infer.sh --test tensorrt/test_infer_trt.py -bs 1 -il 128 --fp16 --num-iters 1003 --encoder ./output/encoder_fp16.engine --decoder ./output/decoder_with_outer_loop_fp16.engine --postnet ./output/postnet_fp16.engine --waveglow ./output/waveglow_fp16.engine --wn-channels 256 diff --git a/demo/Tacotron2/trt/test_infer_trt.py b/demo/Tacotron2/tensorrt/test_infer_trt.py similarity index 84% rename from demo/Tacotron2/trt/test_infer_trt.py rename to demo/Tacotron2/tensorrt/test_infer_trt.py index e78d1737..05031bbe 100644 --- a/demo/Tacotron2/trt/test_infer_trt.py +++ b/demo/Tacotron2/tensorrt/test_infer_trt.py @@ -23,38 +23,37 @@ import argparse import numpy as np from scipy.io.wavfile import write -from inference import checkpoint_from_distributed, unwrap_distributed, MeasureTime, prepare_input_sequence +from inference import checkpoint_from_distributed, unwrap_distributed, MeasureTime, prepare_input_sequence, load_and_setup_model from inference_trt import infer_tacotron2_trt, infer_waveglow_trt -from trt.trt_utils import load_engine +from trt_utils import load_engine import tensorrt as trt import time import dllogger as DLLogger from dllogger import StdOutBackend, JSONStreamBackend, Verbosity -from apex import amp +# from apex import amp def parse_args(parser): """ Parse commandline arguments. """ parser.add_argument('--encoder', type=str, required=True, - help='Full path to the Encoder engine') + help='full path to the Encoder engine') parser.add_argument('--decoder', type=str, required=True, - help='Full path to the DecoderIter engine') + help='full path to the DecoderIter engine') parser.add_argument('--postnet', type=str, required=True, - help='Full path to the Postnet engine') + help='full path to the Postnet engine') parser.add_argument('--waveglow', type=str, required=True, - help='Full path to the WaveGlow engine') + help='full path to the WaveGlow engine') parser.add_argument('--waveglow-ckpt', type=str, default="", - help='Full path to the WaveGlow model checkpoint file') - parser.add_argument('-s', '--sigma-infer', default=0.6, type=float, - help='Standard deviation of the Gaussian distribution') + help='full path to the WaveGlow model checkpoint file') + parser.add_argument('-s', '--sigma-infer', default=0.6, type=float) parser.add_argument('-sr', '--sampling-rate', default=22050, type=int, help='Sampling rate') parser.add_argument('--fp16', action='store_true', - help='Inference with FP16 precision') + help='inference with FP16') parser.add_argument('--log-file', type=str, default='nvlog.json', help='Filename for logging') parser.add_argument('--stft-hop-length', type=int, default=256, @@ -69,34 +68,6 @@ def parse_args(parser): return parser -def load_and_setup_model(model_name, parser, checkpoint, amp_run, to_cuda=True): - model_parser = models.parse_model_args(model_name, parser, add_help=False) - model_args, _ = model_parser.parse_known_args() - - model_config = models.get_model_config(model_name, model_args) - model = models.get_model(model_name, model_config, to_cuda=to_cuda) - - if checkpoint is not None: - if to_cuda: - state_dict = torch.load(checkpoint)['state_dict'] - else: - state_dict = torch.load(checkpoint,map_location='cpu')['state_dict'] - if checkpoint_from_distributed(state_dict): - state_dict = unwrap_distributed(state_dict) - - model.load_state_dict(state_dict) - - if model_name == "WaveGlow": - model = model.remove_weightnorm(model) - - model.eval() - - if amp_run: - model, _ = amp.initialize(model, [], opt_level="O3") - - return model - - def print_stats(measurements_all): print(np.mean(measurements_all['latency'][1:]), @@ -127,7 +98,7 @@ def print_stats(measurements_all): print("Throughput average (samples/sec) = {:.4f}".format(np.mean(throughput))) print("Preprocessing average (seconds) = {:.4f}".format(np.mean(preprocessing))) print("Postprocessing average (seconds) = {:.4f}".format(np.mean(postprocessing))) - print("Number of mels per audio average = {}".format(np.mean(num_mels_per_audio))) + print("Number of mels per audio average = {}".format(np.mean(num_mels_per_audio))) # print("Latency average (seconds) = {:.4f}".format(np.mean(latency))) print("Latency std (seconds) = {:.4f}".format(np.std(latency))) print("Latency cl 50 (seconds) = {:.4f}".format(cf_50)) @@ -180,8 +151,11 @@ def main(): if args.waveglow_ckpt != "": # setup denoiser using WaveGlow PyTorch checkpoint - waveglow_ckpt = load_and_setup_model('WaveGlow', parser, args.waveglow_ckpt, - True, forward_is_infer=True) + waveglow_ckpt = load_and_setup_model('WaveGlow', parser, + args.waveglow_ckpt, + fp16_run=args.fp16, + cpu_run=False, + forward_is_infer=True) denoiser = Denoiser(waveglow_ckpt).cuda() # after initialization, we don't need WaveGlow PyTorch checkpoint # anymore - deleting @@ -215,11 +189,11 @@ def main(): with MeasureTime(measurements, "tacotron2_latency"): mel, mel_lengths = infer_tacotron2_trt(encoder, decoder_iter, postnet, encoder_context, decoder_context, postnet_context, - sequences_padded, input_lengths, measurements, args.fp16) + sequences_padded, input_lengths, measurements, args.fp16, True) with MeasureTime(measurements, "waveglow_latency"): audios = infer_waveglow_trt(waveglow, waveglow_context, mel, measurements, args.fp16) - + num_mels = mel.size(0)*mel.size(2) num_samples = audios.size(0)*audios.size(1) diff --git a/demo/Tacotron2/trt/trt_utils.py b/demo/Tacotron2/tensorrt/trt_utils.py similarity index 88% rename from demo/Tacotron2/trt/trt_utils.py rename to demo/Tacotron2/tensorrt/trt_utils.py index b25a447f..91de22cb 100644 --- a/demo/Tacotron2/trt/trt_utils.py +++ b/demo/Tacotron2/tensorrt/trt_utils.py @@ -16,9 +16,24 @@ import tensorrt as trt +# For a single dimension this will return the min, opt, and max size when given +# input of either one or three (comma delimited) values +# dim="1" or dim=1 returns (1, 1, 1) +# dim="1,4,5" returns (1, 4, 5) +def parse_dynamic_size(dim): + split = str(dim).split(',') + assert len(split) in (1,3) , "Dynamic size input must be either 1 or 3 comma-separated integers" + ints = [int(i) for i in split] + + if len(ints) == 1: + ints *= 3 + + assert ints[0] <= ints[1] <= ints[2] + return tuple(ints) + def is_dimension_dynamic(dim): - return dim is None or dim == -1 + return dim is None or dim <= 0 def is_shape_dynamic(shape): @@ -92,7 +107,6 @@ def engine_info(engine_filepath): def build_engine(model_file, shapes, max_ws=512*1024*1024, fp16=False): TRT_LOGGER = trt.Logger(trt.Logger.WARNING) builder = trt.Builder(TRT_LOGGER) - builder.fp16_mode = fp16 config = builder.create_builder_config() config.max_workspace_size = max_ws diff --git a/demo/Tacotron2/trt/export_onnx2trt.py b/demo/Tacotron2/trt/export_onnx2trt.py deleted file mode 100644 index 7ab5f581..00000000 --- a/demo/Tacotron2/trt/export_onnx2trt.py +++ /dev/null @@ -1,119 +0,0 @@ -# -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# 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 pycuda.driver as cuda -import pycuda.autoinit -import tensorrt as trt -import onnx -import argparse - -import sys -sys.path.append('./') - -from trt.trt_utils import build_engine - -def parse_args(parser): - """ - Parse commandline arguments. - """ - parser.add_argument('-o', '--output', required=True, - help='output folder to save audio (file per phrase)') - parser.add_argument('--encoder', type=str, default="", - help='full path to the Encoder ONNX') - parser.add_argument('--decoder', type=str, default="", - help='full path to the DecoderIter ONNX') - parser.add_argument('--postnet', type=str, default="", - help='full path to the Postnet ONNX') - parser.add_argument('--waveglow', type=str, default="", - help='full path to the WaveGlow ONNX') - parser.add_argument('--fp16', action='store_true', - help='inference with FP16') - - return parser - - -def main(): - - parser = argparse.ArgumentParser( - description='Export from ONNX to TensorRT for Tacotron 2 and WaveGlow') - parser = parse_args(parser) - args = parser.parse_args() - - engine_prec = "_fp16" if args.fp16 else "_fp32" - - # Encoder - shapes=[{"name": "sequences", "min": (1,4), "opt": (1,128), "max": (1,256)}, - {"name": "sequence_lengths", "min": (1,), "opt": (1,), "max": (1,)}] - if args.encoder != "": - print("Building Encoder ...") - encoder_engine = build_engine(args.encoder, shapes=shapes, fp16=args.fp16) - if encoder_engine is not None: - with open(args.output+"/"+"encoder"+engine_prec+".engine", 'wb') as f: - f.write(encoder_engine.serialize()) - else: - print("Failed to build engine from", args.encoder) - sys.exit() - - # DecoderIter - shapes=[{"name": "decoder_input", "min": (1,80), "opt": (1,80), "max": (1,80)}, - {"name": "attention_hidden", "min": (1,1024), "opt": (1,1024), "max": (1,1024)}, - {"name": "attention_cell", "min": (1,1024), "opt": (1,1024), "max": (1,1024)}, - {"name": "decoder_hidden", "min": (1,1024), "opt": (1,1024), "max": (1,1024)}, - {"name": "decoder_cell", "min": (1,1024), "opt": (1,1024), "max": (1,1024)}, - {"name": "attention_weights", "min": (1,4), "opt": (1,128), "max": (1,256)}, - {"name": "attention_weights_cum", "min": (1,4), "opt": (1,128), "max": (1,256)}, - {"name": "attention_context", "min": (1,512), "opt": (1,512), "max": (1,512)}, - {"name": "memory", "min": (1,4,512), "opt": (1,128,512), "max": (1,256,512)}, - {"name": "processed_memory", "min": (1,4,128), "opt": (1,128,128), "max": (1,256,128)}, - {"name": "mask", "min": (1,4), "opt": (1,128), "max": (1,256)}] - if args.decoder != "": - print("Building Decoder ...") - decoder_iter_engine = build_engine(args.decoder, shapes=shapes, fp16=args.fp16) - if decoder_iter_engine is not None: - with open(args.output+"/"+"decoder_iter"+engine_prec+".engine", 'wb') as f: - f.write(decoder_iter_engine.serialize()) - else: - print("Failed to build engine from", args.decoder) - sys.exit() - - # Postnet - shapes=[{"name": "mel_outputs", "min": (1,80,32), "opt": (1,80,768), "max": (1,80,1664)}] - if args.postnet != "": - print("Building Postnet ...") - postnet_engine = build_engine(args.postnet, shapes=shapes, fp16=args.fp16) - if postnet_engine is not None: - with open(args.output+"/"+"postnet"+engine_prec+".engine", 'wb') as f: - f.write(postnet_engine.serialize()) - else: - print("Failed to build engine from", args.postnet) - sys.exit() - - # WaveGlow - shapes=[{"name": "mel", "min": (1,80,32,1), "opt": (1,80,768,1), "max": (1,80,1664,1)}, - {"name": "z", "min": (1,8,1024,1), "opt": (1,8,24576,1), "max": (1,8,53248,1)}] - if args.waveglow != "": - print("Building WaveGlow ...") - waveglow_engine = build_engine(args.waveglow, shapes=shapes, fp16=args.fp16) - if waveglow_engine is not None: - with open(args.output+"/"+"waveglow"+engine_prec+".engine", 'wb') as f: - f.write(waveglow_engine.serialize()) - else: - print("Failed to build engine from", args.waveglow) - sys.exit() - - -if __name__ == '__main__': - main() diff --git a/docker/build.sh b/docker/build.sh index 0799365c..d87e7c03 100755 --- a/docker/build.sh +++ b/docker/build.sh @@ -17,7 +17,7 @@ arg_dockerfile=docker/ubuntu-18.04.Dockerfile arg_imagename=tensorrt-ubuntu -arg_cudaversion=11.1 +arg_cudaversion=11.3.1 arg_help=0 while [[ "$#" -gt 0 ]]; do case $1 in diff --git a/docker/centos-7.Dockerfile b/docker/centos-7.Dockerfile index 83f02945..6aaff682 100644 --- a/docker/centos-7.Dockerfile +++ b/docker/centos-7.Dockerfile @@ -12,19 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -ARG CUDA_VERSION=11.1 +ARG CUDA_VERSION=11.3.1 ARG OS_VERSION=7 FROM nvidia/cuda:${CUDA_VERSION}-cudnn8-devel-centos${OS_VERSION} LABEL maintainer="NVIDIA CORPORATION" -ENV TRT_VERSION 7.2.3.4 +ENV TRT_VERSION 8.0.1.6 SHELL ["/bin/bash", "-c"] # Setup user account ARG uid=1000 ARG gid=1000 -RUN groupadd -r -f -g ${gid} trtuser && useradd -r -u ${uid} -g ${gid} -ms /bin/bash trtuser +RUN groupadd -r -f -g ${gid} trtuser && useradd -o -r -u ${uid} -g ${gid} -ms /bin/bash trtuser RUN usermod -aG wheel trtuser RUN echo 'trtuser:nvidia' | chpasswd RUN mkdir -p /workspace && chown trtuser /workspace @@ -44,23 +44,22 @@ RUN yum -y install \ sudo # Install python3 -RUN cd /tmp &&\ - curl -O https://www.python.org/ftp/python/3.8.3/Python-3.8.3.tgz &&\ - tar -xzf Python-3.8.3.tgz && cd Python-3.8.3 &&\ - ./configure --enable-optimizations && make altinstall &&\ - rm -rf /tmp/Python-3.8.3 +RUN yum install -y python36 python3-devel # Install TensorRT -RUN cd /tmp &&\ - wget https://developer.download.nvidia.com/compute/machine-learning/repos/rhel7/x86_64/nvidia-machine-learning-repo-rhel7-1.0.0-1.x86_64.rpm &&\ - rpm -Uvh nvidia-machine-learning-repo-*.rpm -RUN yum install -y libnvinfer7 libnvparsers7 libnvinfer-plugin7 libnvonnxparsers7 libnvinfer-devel libnvparsers-devel libnvinfer-plugin-devel python3-libnvinfer +RUN v="${TRT_VERSION%.*}-1.cuda${CUDA_VERSION%.*}" &&\ + yum-config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel7/x86_64/cuda-rhel7.repo &&\ + yum -y install libnvinfer8-${v} libnvparsers8-${v} libnvonnxparsers8-${v} libnvinfer-plugin8-${v} \ + libnvinfer-devel-${v} libnvparsers-devel-${v} libnvonnxparsers-devel-${v} libnvinfer-plugin-devel-${v} \ + python3-libnvinfer-${v} # Install PyPI packages RUN pip3 install --upgrade pip RUN pip3 install setuptools>=41.0.0 +RUN pip3 install numpy COPY requirements.txt /tmp/requirements.txt RUN pip3 install -r /tmp/requirements.txt +RUN pip3 install jupyter jupyterlab # Install Cmake RUN cd /tmp && \ @@ -72,6 +71,8 @@ RUN cd /tmp && \ # Download NGC client RUN cd /usr/local/bin && wget https://ngc.nvidia.com/downloads/ngccli_cat_linux.zip && unzip ngccli_cat_linux.zip && chmod u+x ngc && rm ngccli_cat_linux.zip ngc.md5 && echo "no-apikey\nascii\n" | ngc config set +RUN rm /usr/bin/python && ln -s /usr/bin/python3 /usr/bin/python + # Set environment and working directory ENV TRT_LIBPATH /usr/lib/x86_64-linux-gnu ENV TRT_OSSPATH /workspace/TensorRT diff --git a/docker/centos-8.Dockerfile b/docker/centos-8.Dockerfile new file mode 100644 index 00000000..277a233d --- /dev/null +++ b/docker/centos-8.Dockerfile @@ -0,0 +1,83 @@ +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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. + +ARG CUDA_VERSION=11.3.1 +ARG OS_VERSION=8 + +FROM nvidia/cuda:${CUDA_VERSION}-cudnn8-devel-centos${OS_VERSION} +LABEL maintainer="NVIDIA CORPORATION" + +ENV TRT_VERSION 8.0.1.6 +SHELL ["/bin/bash", "-c"] + +# Setup user account +ARG uid=1000 +ARG gid=1000 +RUN groupadd -r -f -g ${gid} trtuser && useradd -o -r -u ${uid} -g ${gid} -ms /bin/bash trtuser +RUN usermod -aG wheel trtuser +RUN echo 'trtuser:nvidia' | chpasswd +RUN mkdir -p /workspace && chown trtuser /workspace + +# Install requried packages +RUN yum -y groupinstall "Development Tools" +RUN yum -y install \ + openssl-devel \ + bzip2-devel \ + libffi-devel \ + zlib-devel \ + wget \ + perl-core \ + git \ + pkg-config \ + unzip \ + sudo + +# Install python3 +RUN yum install -y python3-devel + +# Install TensorRT +RUN v="${TRT_VERSION%.*}-1.cuda${CUDA_VERSION%.*}" &&\ + dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.repo &&\ + yum -y install libnvinfer8-${v} libnvparsers8-${v} libnvonnxparsers8-${v} libnvinfer-plugin8-${v} \ + libnvinfer-devel-${v} libnvparsers-devel-${v} libnvonnxparsers-devel-${v} libnvinfer-plugin-devel-${v} \ + python3-libnvinfer-${v} + +# Install PyPI packages +RUN pip3 install --upgrade pip +RUN pip3 install setuptools>=41.0.0 +RUN pip3 install numpy +COPY requirements.txt /tmp/requirements.txt +RUN pip3 install -r /tmp/requirements.txt +RUN pip3 install jupyter jupyterlab + +# Install Cmake +RUN cd /tmp && \ + wget https://github.com/Kitware/CMake/releases/download/v3.14.4/cmake-3.14.4-Linux-x86_64.sh && \ + chmod +x cmake-3.14.4-Linux-x86_64.sh && \ + ./cmake-3.14.4-Linux-x86_64.sh --prefix=/usr/local --exclude-subdir --skip-license && \ + rm ./cmake-3.14.4-Linux-x86_64.sh + +# Download NGC client +RUN cd /usr/local/bin && wget https://ngc.nvidia.com/downloads/ngccli_cat_linux.zip && unzip ngccli_cat_linux.zip && chmod u+x ngc && rm ngccli_cat_linux.zip ngc.md5 && echo "no-apikey\nascii\n" | ngc config set + +RUN ln -s /usr/bin/python3 /usr/bin/python + +# Set environment and working directory +ENV TRT_LIBPATH /usr/lib/x86_64-linux-gnu +ENV TRT_OSSPATH /workspace/TensorRT +ENV LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${TRT_OSSPATH}/build/out:${TRT_LIBPATH}" +WORKDIR /workspace + +USER trtuser +RUN ["/bin/bash"] diff --git a/docker/launch.sh b/docker/launch.sh index ccfdf057..551b2038 100755 --- a/docker/launch.sh +++ b/docker/launch.sh @@ -17,11 +17,13 @@ arg_tag=tensorrt-ubuntu20.04 arg_gpus=all +arg_jupyter=0 arg_help=0 while [[ "$#" -gt 0 ]]; do case $1 in --tag) arg_tag="$2"; shift;; --gpus) arg_gpus="$2"; shift;; + --jupyter) arg_jupyter="$2"; shift;; -h|--help) arg_help=1;; *) echo "Unknown parameter passed: $1"; echo "For help type: $0 --help"; exit 1; esac; shift; done @@ -31,6 +33,7 @@ if [ "$arg_help" -eq "1" ]; then echo " --help or -h : Print this help menu." echo " --tag : Image name for generated container." echo " --gpus : Number of GPUs visible in container. Set 'none' to disable, and 'all' to make all visible." + echo " --jupyter : Launch Jupyter notebook using the specified port number." exit; fi @@ -39,8 +42,16 @@ if [ "$arg_gpus" != "none" ]; then extra_args="$extra_args --gpus $arg_gpus" fi +if [ "$arg_jupyter" -ne "0" ]; then + extra_args+=" -p $arg_jupyter:$arg_jupyter" +fi + docker_args="$extra_args -v ${PWD}:/workspace/TensorRT --rm -it $arg_tag:latest" +if [ "$arg_jupyter" -ne "0" ]; then + docker_args+=" jupyter-lab --port=$arg_jupyter --no-browser --ip 0.0.0.0 --allow-root" +fi + echo "Launching container:" echo "> docker run $docker_args" docker run $docker_args diff --git a/docker/patch/centos-python-ssl.patch b/docker/patch/centos-python-ssl.patch deleted file mode 100644 index 2a7619be..00000000 --- a/docker/patch/centos-python-ssl.patch +++ /dev/null @@ -1,4 +0,0 @@ -SSL=/usr/local/ssl -_ssl _ssl.c \ - -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \ - -L$(SSL)/lib -lssl -lcrypto diff --git a/docker/ubuntu-18.04.Dockerfile b/docker/ubuntu-18.04.Dockerfile index c36d4fa2..994111e1 100644 --- a/docker/ubuntu-18.04.Dockerfile +++ b/docker/ubuntu-18.04.Dockerfile @@ -12,13 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -ARG CUDA_VERSION=11.1 +ARG CUDA_VERSION=11.3.1 ARG OS_VERSION=18.04 FROM nvidia/cuda:${CUDA_VERSION}-cudnn8-devel-ubuntu${OS_VERSION} LABEL maintainer="NVIDIA CORPORATION" -ENV TRT_VERSION 7.2.3.4 +ENV TRT_VERSION 8.0.1.6 SHELL ["/bin/bash", "-c"] # Setup user account @@ -62,18 +62,21 @@ RUN apt-get install -y --no-install-recommends \ ln -s /usr/bin/pip3 pip; # Install TensorRT -RUN cd /tmp &&\ - wget https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/nvidia-machine-learning-repo-ubuntu1804_1.0.0-1_amd64.deb &&\ - dpkg -i nvidia-machine-learning-repo-*.deb && apt-get update RUN v="${TRT_VERSION%.*}-1+cuda${CUDA_VERSION%.*}" &&\ - apt-get install -y libnvinfer7=${v} libnvinfer-plugin7=${v} libnvparsers7=${v} libnvonnxparsers7=${v} libnvinfer-dev=${v} libnvinfer-plugin-dev=${v} libnvparsers-dev=${v} python3-libnvinfer=${v} &&\ - apt-mark hold libnvinfer7 libnvinfer-plugin7 libnvparsers7 libnvonnxparsers7 libnvinfer-dev libnvinfer-plugin-dev libnvparsers-dev python3-libnvinfer + apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub &&\ + apt-get update &&\ + sudo apt-get install libnvinfer8=${v} libnvonnxparsers8=${v} libnvparsers8=${v} libnvinfer-plugin8=${v} \ + libnvinfer-dev=${v} libnvonnxparsers-dev=${v} libnvparsers-dev=${v} libnvinfer-plugin-dev=${v} \ + python3-libnvinfer=${v} # Install PyPI packages RUN pip3 install --upgrade pip RUN pip3 install setuptools>=41.0.0 COPY requirements.txt /tmp/requirements.txt RUN pip3 install -r /tmp/requirements.txt +RUN pip3 install jupyter jupyterlab +# Workaround to remove numpy installed with tensorflow +RUN pip3 install --upgrade numpy # Install Cmake RUN cd /tmp && \ diff --git a/docker/ubuntu-16.04.Dockerfile b/docker/ubuntu-20.04.Dockerfile similarity index 70% rename from docker/ubuntu-16.04.Dockerfile rename to docker/ubuntu-20.04.Dockerfile index c9564f47..ba09e236 100644 --- a/docker/ubuntu-16.04.Dockerfile +++ b/docker/ubuntu-20.04.Dockerfile @@ -12,13 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -ARG CUDA_VERSION=11.1 -ARG OS_VERSION=16.04 +ARG CUDA_VERSION=11.3.1 +ARG OS_VERSION=20.04 FROM nvidia/cuda:${CUDA_VERSION}-cudnn8-devel-ubuntu${OS_VERSION} LABEL maintainer="NVIDIA CORPORATION" -ENV TRT_VERSION 7.2.2.3 +ENV TRT_VERSION 8.0.1.6 SHELL ["/bin/bash", "-c"] # Setup user account @@ -29,6 +29,9 @@ RUN usermod -aG sudo trtuser RUN echo 'trtuser:nvidia' | chpasswd RUN mkdir -p /workspace && chown trtuser /workspace +# Required to build Ubuntu 20.04 without user prompts with DLFW container +ENV DEBIAN_FRONTEND=noninteractive + # Install requried libraries RUN apt-get update && apt-get install -y software-properties-common RUN add-apt-repository ppa:ubuntu-toolchain-r/test @@ -52,27 +55,31 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential # Install python3 -RUN add-apt-repository ppa:deadsnakes/ppa && apt-get update &&\ - apt-get remove -y python3 python && apt-get autoremove -y &&\ - apt-get install -y python3.6 python3.6-dev &&\ - cd /tmp && wget https://bootstrap.pypa.io/get-pip.py && python3.6 get-pip.py &&\ - python3.6 -m pip install wheel &&\ - ln -s /usr/bin/python3.6 /usr/bin/python3 &&\ - ln -s /usr/bin/python3.6 /usr/bin/python +RUN apt-get install -y --no-install-recommends \ + python3 \ + python3-pip \ + python3-dev \ + python3-wheel &&\ + cd /usr/local/bin &&\ + ln -s /usr/bin/python3 python &&\ + ln -s /usr/bin/pip3 pip; # Install TensorRT -RUN cd /tmp &&\ - wget https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1604/x86_64/nvidia-machine-learning-repo-ubuntu1604_1.0.0-1_amd64.deb &&\ - dpkg -i nvidia-machine-learning-repo-*.deb && apt-get update RUN v="${TRT_VERSION%.*}-1+cuda${CUDA_VERSION%.*}" &&\ - apt-get install -y libnvinfer7=${v} libnvinfer-plugin7=${v} libnvparsers7=${v} libnvonnxparsers7=${v} libnvinfer-dev=${v} libnvinfer-plugin-dev=${v} libnvparsers-dev=${v} python3-libnvinfer=${v} &&\ - apt-mark hold libnvinfer7 libnvinfer-plugin7 libnvparsers7 libnvonnxparsers7 libnvinfer-dev libnvinfer-plugin-dev libnvparsers-dev python3-libnvinfer + apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/7fa2af80.pub &&\ + apt-get update &&\ + sudo apt-get install libnvinfer8=${v} libnvonnxparsers8=${v} libnvparsers8=${v} libnvinfer-plugin8=${v} \ + libnvinfer-dev=${v} libnvonnxparsers-dev=${v} libnvparsers-dev=${v} libnvinfer-plugin-dev=${v} \ + python3-libnvinfer=${v} # Install PyPI packages RUN pip3 install --upgrade pip RUN pip3 install setuptools>=41.0.0 COPY requirements.txt /tmp/requirements.txt RUN pip3 install -r /tmp/requirements.txt +RUN pip3 install jupyter jupyterlab +# Workaround to remove numpy installed with tensorflow +RUN pip3 install --upgrade numpy # Install Cmake RUN cd /tmp && \ diff --git a/docker/ubuntu-cross-aarch64.Dockerfile b/docker/ubuntu-cross-aarch64.Dockerfile index d478aa9a..a4fe68f9 100644 --- a/docker/ubuntu-cross-aarch64.Dockerfile +++ b/docker/ubuntu-cross-aarch64.Dockerfile @@ -14,10 +14,12 @@ ARG CUDA_VERSION=10.2 ARG OS_VERSION=18.04 -FROM nvidia/cuda:${CUDA_VERSION}-devel-ubuntu${OS_VERSION} +FROM nvidia/cuda:${CUDA_VERSION}-devel-ubuntu${OS_VERSION} LABEL maintainer="NVIDIA CORPORATION" +ENV TRT_VERSION 8.0.1.6 + ARG uid=1000 ARG gid=1000 RUN groupadd -r -f -g ${gid} trtuser && useradd -o -r -u ${uid} -g ${gid} -ms /bin/bash trtuser @@ -94,15 +96,18 @@ RUN dpkg -x /pdk_files/libcudnn[7-8]_*-1+cuda10.[0-9]_arm64.deb /pdk_files/cudn && ln -s /pdk_files/cudnn/usr/include/aarch64-linux-gnu/cudnn_version_v[7-9].h /usr/include/cudnn_version.h # Unpack libnvinfer -RUN dpkg -x /pdk_files/libnvinfer[0-7]_*-1+cuda10.[0-9]_arm64.deb /pdk_files/tensorrt \ +RUN dpkg -x /pdk_files/libnvinfer[0-8]_*-1+cuda10.[0-9]_arm64.deb /pdk_files/tensorrt \ && dpkg -x /pdk_files/libnvinfer-dev_*-1+cuda10.[0-9]_arm64.deb /pdk_files/tensorrt \ - && dpkg -x /pdk_files/libnvparsers[6-7]_*-1+cuda10.[0-9]_arm64.deb /pdk_files/tensorrt \ + && dpkg -x /pdk_files/libnvparsers[6-8]_*-1+cuda10.[0-9]_arm64.deb /pdk_files/tensorrt \ && dpkg -x /pdk_files/libnvparsers-dev_*-1+cuda10.[0-9]_arm64.deb /pdk_files/tensorrt \ - && dpkg -x /pdk_files/libnvinfer-plugin[6-7]_*-1+cuda10.[0-9]_arm64.deb /pdk_files/tensorrt \ + && dpkg -x /pdk_files/libnvinfer-plugin[6-8]_*-1+cuda10.[0-9]_arm64.deb /pdk_files/tensorrt \ && dpkg -x /pdk_files/libnvinfer-plugin-dev_*-1+cuda10.[0-9]_arm64.deb /pdk_files/tensorrt \ - && dpkg -x /pdk_files/libnvonnxparsers[6-7]_*-1+cuda10.[0-9]_arm64.deb /pdk_files/tensorrt \ + && dpkg -x /pdk_files/libnvonnxparsers[6-8]_*-1+cuda10.[0-9]_arm64.deb /pdk_files/tensorrt \ && dpkg -x /pdk_files/libnvonnxparsers-dev_*-1+cuda10.[0-9]_arm64.deb /pdk_files/tensorrt +# Clean up debs +RUN rm -rf /pdk_files/*.deb + # create stub libraries RUN cd /pdk_files/tensorrt \ && ln -s usr/include/aarch64-linux-gnu include \ diff --git a/docker/ubuntu-cross-ppc64le.Dockerfile b/docker/ubuntu-cross-ppc64le.Dockerfile deleted file mode 100755 index 9771a959..00000000 --- a/docker/ubuntu-cross-ppc64le.Dockerfile +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# 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. - -ARG CUDA_VERSION=11.0 -ARG OS_VERSION=18.04 -FROM nvidia/cuda:${CUDA_VERSION}-cudnn8-devel-ubuntu${OS_VERSION} - -LABEL maintainer="NVIDIA CORPORATION" - -ARG uid=1000 -ARG gid=1000 -RUN groupadd -r -f -g ${gid} trtuser && useradd -o -r -u ${uid} -g ${gid} -ms /bin/bash trtuser -RUN usermod -aG sudo trtuser -RUN echo 'trtuser:nvidia' | chpasswd -RUN mkdir -p /workspace && chown -R trtuser:trtuser /workspace - -# Install requried libraries -RUN apt-get update && apt-get install -y software-properties-common -RUN add-apt-repository ppa:ubuntu-toolchain-r/test -RUN apt-get update && apt-get install -y --no-install-recommends \ - libcurl4-openssl-dev \ - wget \ - zlib1g-dev \ - git \ - pkg-config \ - python3 \ - python3-pip \ - python3-dev \ - python3-setuptools \ - python3-wheel \ - sudo \ - ssh \ - pbzip2 \ - pv \ - bzip2 \ - unzip \ - g++-powerpc64le-linux-gnu \ - libc6-powerpc-cross - -RUN cd /usr/local/bin &&\ - ln -s /usr/bin/python3 python &&\ - ln -s /usr/bin/pip3 pip - -RUN cd /tmp && \ - wget https://github.com/Kitware/CMake/releases/download/v3.14.4/cmake-3.14.4-Linux-x86_64.sh && \ - chmod +x cmake-3.14.4-Linux-x86_64.sh && \ - ./cmake-3.14.4-Linux-x86_64.sh --prefix=/usr/local --exclude-subdir --skip-license && \ - rm ./cmake-3.14.4-Linux-x86_64.sh -# Download ppc Cudnn, Cublas, Cudart, RT, nvprof -# TODO Remove once packages are added to cuda cross compiler -RUN wget http://cuda-repo/release-candidates/Libraries/cuDNN/v8.0/8.0.2.5_20200617_28575977/11.0.x-r445/Installer/Ubuntu18_04-ppc64le/libcudnn8_8.0.2.5-1+cuda11.0_ppc64el.deb && \ - wget http://cuda-repo/release-candidates/Libraries/cuDNN/v8.0/8.0.2.5_20200617_28575977/11.0.x-r445/Installer/Ubuntu18_04-ppc64le/libcudnn8-dev_8.0.2.5-1+cuda11.0_ppc64el.deb && \ - wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/ppc64el/libcublas-dev-11-0_11.0.0.191-1_ppc64el.deb && \ - wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/ppc64el/libcublas-11-0_11.0.0.191-1_ppc64el.deb && \ - wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/ppc64el/cuda-cudart-11-0_11.0.171-1_ppc64el.deb && \ - wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/ppc64el/cuda-cudart-dev-11-0_11.0.171-1_ppc64el.deb && \ - wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/ppc64el/cuda-nvrtc-11-0_11.0.167-1_ppc64el.deb && \ - wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/ppc64el/cuda-nvrtc-dev-11-0_11.0.167-1_ppc64el.deb && \ - wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/ppc64el/cuda-nvcc-11-0_11.0.167-1_ppc64el.deb && \ - wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/ppc64el/cuda-nvprof-11-0_11.0.167-1_ppc64el.deb - -# Unpack Cublas -RUN dpkg -x libcublas-11-0_11.0.0.191-1_ppc64el.deb cublas && \ - dpkg -x libcublas-dev-11-0_11.0.0.191-1_ppc64el.deb cublas && \ - cp -r cublas/* / - -# Unpack Cudart -RUN dpkg -x cuda-cudart-11-0_11.0.171-1_ppc64el.deb cudart && \ - dpkg -x cuda-cudart-dev-11-0_11.0.171-1_ppc64el.deb cudart && \ - cp -r cudart/* / - -# Unpack RT -RUN dpkg -x cuda-nvrtc-11-0_11.0.167-1_ppc64el.deb rt && \ - dpkg -x cuda-nvrtc-dev-11-0_11.0.167-1_ppc64el.deb rt && \ - cp -r rt/* / - -# Unpack Cudnn -RUN dpkg -x libcudnn8_8.0.2.5-1+cuda11.0_ppc64el.deb cudnn && \ - dpkg -x libcudnn8-dev_8.0.2.5-1+cuda11.0_ppc64el.deb cudnn && \ - cp -r cudnn/* / - -# Unpack NVCC, and copy headers -RUN dpkg -x cuda-nvcc-11-0_11.0.167-1_ppc64el.deb nvcc && \ - cp -r nvcc/usr/local/cuda-11.0/targets/ppc64le-linux/include/* /usr/local/cuda-11.0/targets/ppc64le-linux/include/ - -# Install nvprof -RUN dpkg -x cuda-nvprof-11-0_11.0.167-1_ppc64el.deb prof && \ - cp -r prof/* / - -# Clean up temporary files -RUN rm -rf cublas cudart rt prof nvcc -RUN rm *.deb - -WORKDIR /workspace -ENV TRT_LIBPATH /usr/lib/x86_64-linux-gnu -ENV TRT_OSSPATH /workspace/TensorRT -USER trtuser -RUN ["/bin/bash"] diff --git a/include/NvCaffeParser.h b/include/NvCaffeParser.h index 520aeb20..495bf414 100644 --- a/include/NvCaffeParser.h +++ b/include/NvCaffeParser.h @@ -53,7 +53,7 @@ public: //! //! \return ITensor* corresponding to the queried name. If no such ITensor exists, then nullptr is returned. //! - virtual nvinfer1::ITensor* find(const char* name) const TRTNOEXCEPT = 0; + virtual nvinfer1::ITensor* find(const char* name) const noexcept = 0; protected: virtual ~IBlobNameToTensor() {} @@ -71,62 +71,16 @@ protected: class IBinaryProtoBlob { public: - virtual const void* getData() TRTNOEXCEPT = 0; - virtual nvinfer1::DimsNCHW getDimensions() TRTNOEXCEPT = 0; - virtual nvinfer1::DataType getDataType() TRTNOEXCEPT = 0; - virtual void destroy() TRTNOEXCEPT = 0; - -protected: - virtual ~IBinaryProtoBlob() {} -}; - -//! -//! \class IPluginFactory -//! -//! \brief Plugin factory used to configure plugins. -//! -class IPluginFactory -{ -public: + virtual const void* getData() noexcept = 0; + virtual nvinfer1::Dims4 getDimensions() noexcept = 0; + virtual nvinfer1::DataType getDataType() noexcept = 0; //! - //! \brief A user implemented function that determines if a layer configuration is provided by an IPlugin. + //! \deprecated Deprecated interface will be removed in TensorRT 10.0. //! - //! \param layerName Name of the layer which the user wishes to validate. + //! \warning Calling destroy on a managed pointer will result in a double-free error. //! - virtual bool isPlugin(const char* layerName) TRTNOEXCEPT = 0; - - //! - //! \brief Creates a plugin. - //! - //! \param layerName Name of layer associated with the plugin. - //! \param weights Weights used for the layer. - //! \param nbWeights Number of weights. - //! - virtual nvinfer1::IPlugin* createPlugin( - const char* layerName, const nvinfer1::Weights* weights, int32_t nbWeights) TRTNOEXCEPT = 0; - - virtual ~IPluginFactory() {} -}; - -//! -//! \class IPluginFactoryExt -//! -//! \brief Plugin factory used to configure plugins with added support for TRT versioning. -//! -class IPluginFactoryExt : public IPluginFactory -{ -public: - virtual int32_t getVersion() const TRTNOEXCEPT - { - return NV_TENSORRT_VERSION; - } - - //! - //! \brief A user implemented function that determines if a layer configuration is provided by an IPluginExt. - //! - //! \param layerName Name of the layer which the user wishes to validate. - //! - virtual bool isPluginExt(const char* layerName) TRTNOEXCEPT = 0; + TRT_DEPRECATED virtual void destroy() noexcept = 0; + virtual ~IBinaryProtoBlob() noexcept = default; }; //! @@ -142,7 +96,7 @@ public: //! //! \param layerName Name of the layer which the user wishes to validate. //! - virtual bool isPluginV2(const char* layerName) TRTNOEXCEPT = 0; + virtual bool isPluginV2(const char* layerName) noexcept = 0; //! //! \brief Creates a plugin. @@ -153,9 +107,10 @@ public: //! \param libNamespace Library Namespace associated with the plugin object //! virtual nvinfer1::IPluginV2* createPlugin(const char* layerName, const nvinfer1::Weights* weights, - int32_t nbWeights, const char* libNamespace = "") TRTNOEXCEPT = 0; + int32_t nbWeights, const char* libNamespace = "") noexcept + = 0; - virtual ~IPluginFactoryV2() {} + virtual ~IPluginFactoryV2() noexcept = default; }; //! //! \class ICaffeParser @@ -182,13 +137,12 @@ public: //! //! \see nvcaffeparser1::IBlobNameToTensor //! - virtual const IBlobNameToTensor* parse(const char* deploy, - const char* model, - nvinfer1::INetworkDefinition& network, - nvinfer1::DataType weightType) TRTNOEXCEPT = 0; + virtual const IBlobNameToTensor* parse(const char* deploy, const char* model, nvinfer1::INetworkDefinition& network, + nvinfer1::DataType weightType) noexcept + = 0; //! - //! \brief Parse a deploy prototxt a binaryproto Caffe model from memory buffers to extract + //! \brief Parse a deploy prototxt and a binaryproto Caffe model from memory buffers to extract //! network definition and weights associated with the network, respectively. //! //! \param deployBuffer The plain text deploy prototxt used to define the network definition. @@ -202,12 +156,10 @@ public: //! //! \see nvcaffeparser1::IBlobNameToTensor //! - virtual const IBlobNameToTensor* parseBuffers(const char* deployBuffer, - std::size_t deployLength, - const char* modelBuffer, - std::size_t modelLength, - nvinfer1::INetworkDefinition& network, - nvinfer1::DataType weightType) TRTNOEXCEPT = 0; + virtual const IBlobNameToTensor* parseBuffers(const char* deployBuffer, std::size_t deployLength, + const char* modelBuffer, std::size_t modelLength, nvinfer1::INetworkDefinition& network, + nvinfer1::DataType weightType) noexcept + = 0; //! //! \brief Parse and extract data stored in binaryproto file. @@ -221,7 +173,7 @@ public: //! //! \see nvcaffeparser1::IBinaryProtoBlob //! - virtual IBinaryProtoBlob* parseBinaryProto(const char* fileName) TRTNOEXCEPT = 0; + virtual IBinaryProtoBlob* parseBinaryProto(const char* fileName) noexcept = 0; //! //! \brief Set buffer size for the parsing and storage of the learned model. @@ -230,41 +182,30 @@ public: //! //! \note Default size is 2^30 bytes. //! - virtual void setProtobufBufferSize(size_t size) TRTNOEXCEPT = 0; - - //! - //! \brief Set the IPluginFactory used to create the user defined plugins. - //! - //! \param factory Pointer to an instance of the user implmentation of IPluginFactory. - //! - virtual void setPluginFactory(IPluginFactory* factory) TRTNOEXCEPT = 0; - - //! - //! \brief Set the IPluginFactoryExt used to create the user defined pluginExts. - //! - //! \param factory Pointer to an instance of the user implmentation of IPluginFactoryExt. - //! - virtual void setPluginFactoryExt(IPluginFactoryExt* factory) TRTNOEXCEPT = 0; + virtual void setProtobufBufferSize(size_t size) noexcept = 0; //! //! \brief Destroy this ICaffeParser object. //! - virtual void destroy() TRTNOEXCEPT = 0; + //! \deprecated Deprecated interface will be removed in TensorRT 10.0. + //! + //! \warning Calling destroy on a managed pointer will result in a double-free error. + //! + TRT_DEPRECATED virtual void destroy() noexcept = 0; //! //! \brief Set the IPluginFactoryV2 used to create the user defined pluginV2 objects. //! - //! \param factory Pointer to an instance of the user implmentation of IPluginFactoryV2. + //! \param factory Pointer to an instance of the user implementation of IPluginFactoryV2. //! - virtual void setPluginFactoryV2(IPluginFactoryV2* factory) TRTNOEXCEPT = 0; + virtual void setPluginFactoryV2(IPluginFactoryV2* factory) noexcept = 0; //! //! \brief Set the namespace used to lookup and create plugins in the network. //! - virtual void setPluginNamespace(const char* libNamespace) TRTNOEXCEPT = 0; + virtual void setPluginNamespace(const char* libNamespace) noexcept = 0; -protected: - virtual ~ICaffeParser() {} + virtual ~ICaffeParser() noexcept = default; public: //! @@ -275,23 +216,25 @@ public: //! recorder to nullptr unregisters the recorder with the interface, resulting in a call to decRefCount if //! a recorder has been registered. //! + //! If an error recorder is not set, messages will be sent to the global log stream. + //! //! \param recorder The error recorder to register with this interface. //! - //! \see getErrorRecorder + //! \see getErrorRecorder() //! - virtual void setErrorRecorder(nvinfer1::IErrorRecorder* recorder) TRTNOEXCEPT = 0; + virtual void setErrorRecorder(nvinfer1::IErrorRecorder* recorder) noexcept = 0; //! //! \brief get the ErrorRecorder assigned to this interface. //! - //! Retrieves the assigned error recorder object for the given class. A default error recorder does not exist, - //! so a nullptr will be returned if setErrorRecorder has not been called. + //! Retrieves the assigned error recorder object for the given class. A + //! nullptr will be returned if setErrorRecorder has not been called. //! //! \return A pointer to the IErrorRecorder object that has been registered. //! - //! \see setErrorRecorder + //! \see setErrorRecorder() //! - virtual nvinfer1::IErrorRecorder* getErrorRecorder() const TRTNOEXCEPT = 0; + virtual nvinfer1::IErrorRecorder* getErrorRecorder() const noexcept = 0; }; //! @@ -301,19 +244,22 @@ public: //! //! \see nvcaffeparser1::ICaffeParser //! -TENSORRTAPI ICaffeParser* createCaffeParser() TRTNOEXCEPT; +//! \deprecated ICaffeParser will be removed in TensorRT 9.0. Plan to migrate your workflow to +//! use nvonnxparser::IParser for deployment. +//! +TENSORRTAPI ICaffeParser* createCaffeParser() noexcept; //! //! \brief Shuts down protocol buffers library. //! //! \note No part of the protocol buffers library can be used after this function is called. //! -TENSORRTAPI void shutdownProtobufLibrary() TRTNOEXCEPT; +TENSORRTAPI void shutdownProtobufLibrary() noexcept; } // namespace nvcaffeparser1 //! //! Internal C entry point for creating ICaffeParser. //! @private //! -extern "C" TENSORRTAPI void* createNvCaffeParser_INTERNAL(); +extern "C" TENSORRTAPI void* createNvCaffeParser_INTERNAL() noexcept; #endif diff --git a/include/NvInfer.h b/include/NvInfer.h index bbbd24f1..ca722190 100644 --- a/include/NvInfer.h +++ b/include/NvInfer.h @@ -17,6 +17,7 @@ #ifndef NV_INFER_H #define NV_INFER_H +#include "NvInferLegacyDims.h" #include "NvInferRuntime.h" //! @@ -45,382 +46,6 @@ namespace nvinfer1 { -//! -//! \class Dims2 -//! \brief Descriptor for two-dimensional data. -//! -class Dims2 : public Dims -{ -public: - //! - //! \brief Construct an empty Dims2 object. - //! - Dims2() - { - nbDims = 2; - d[0] = d[1] = 0; - } - - //! - //! \brief Construct a Dims2 from 2 elements. - //! - //! \param d0 The first element. - //! \param d1 The second element. - //! - Dims2(int32_t d0, int32_t d1) - { - nbDims = 2; - d[0] = d0; - d[1] = d1; - } -}; - -//! -//! \class DimsHW -//! \brief Descriptor for two-dimensional spatial data. -//! -class DimsHW : public Dims2 -{ -public: - //! - //! \brief Construct an empty DimsHW object. - //! - DimsHW() - : Dims2() - { - type[0] = type[1] = DimensionType::kSPATIAL; - } - - //! - //! \brief Construct a DimsHW given height and width. - //! - //! \param Height the height of the data - //! \param Width the width of the data - //! - DimsHW(int32_t height, int32_t width) - : Dims2(height, width) - { - type[0] = type[1] = DimensionType::kSPATIAL; - } - - //! - //! \brief Get the height. - //! - //! \return The height. - //! - int32_t& h() - { - return d[0]; - } - - //! - //! \brief Get the height. - //! - //! \return The height. - //! - int32_t h() const - { - return d[0]; - } - - //! - //! \brief Get the width. - //! - //! \return The width. - //! - int32_t& w() - { - return d[1]; - } - - //! - //! \brief Get the width. - //! - //! \return The width. - //! - int32_t w() const - { - return d[1]; - } -}; - -//! -//! \class Dims3 -//! \brief Descriptor for three-dimensional data. -//! -class Dims3 : public Dims -{ -public: - //! - //! \brief Construct an empty Dims3 object. - //! - Dims3() - { - nbDims = 3; - d[0] = d[1] = d[2] = 0; - } - - //! - //! \brief Construct a Dims3 from 3 elements. - //! - //! \param d0 The first element. - //! \param d1 The second element. - //! \param d2 The third element. - //! - Dims3(int32_t d0, int32_t d1, int32_t d2) - { - nbDims = 3; - d[0] = d0; - d[1] = d1; - d[2] = d2; - } -}; - -//! -//! \class DimsCHW -//! \brief Descriptor for data with one channel dimension and two spatial dimensions. -//! -//! \deprecated DimsCHW will be removed in TensorRT 8.0, use Dims3 instead. -//! -class TRT_DEPRECATED DimsCHW : public Dims3 -{ -public: - //! - //! \brief Construct an empty DimsCHW object. - //! - DimsCHW() - : Dims3() - { - type[0] = DimensionType::kCHANNEL; - type[1] = type[2] = DimensionType::kSPATIAL; - } - - //! - //! \brief Construct a DimsCHW given channel count, height and width. - //! - //! \param channels The channel count. - //! \param height The height of the data. - //! \param width The width of the data. - //! - DimsCHW(int32_t channels, int32_t height, int32_t width) - : Dims3(channels, height, width) - { - type[0] = DimensionType::kCHANNEL; - type[1] = type[2] = DimensionType::kSPATIAL; - } - - //! - //! \brief Get the channel count. - //! - //! \return The channel count. - //! - int32_t& c() - { - return d[0]; - } - - //! - //! \brief Get the channel count. - //! - //! \return The channel count. - //! - int32_t c() const - { - return d[0]; - } - - //! - //! \brief Get the height. - //! - //! \return The height. - //! - int32_t& h() - { - return d[1]; - } - - //! - //! \brief Get the height. - //! - //! \return The height. - //! - int32_t h() const - { - return d[1]; - } - - //! - //! \brief Get the width. - //! - //! \return The width. - //! - int32_t& w() - { - return d[2]; - } - - //! - //! \brief Get the width. - //! - //! \return The width. - //! - int32_t w() const - { - return d[2]; - } -}; - -//! -//! \class Dims4 -//! \brief Descriptor for four-dimensional data. -//! -class Dims4 : public Dims -{ -public: - //! - //! \brief Construct an empty Dims2 object. - //! - Dims4() - { - nbDims = 4; - d[0] = d[1] = d[2] = d[3] = 0; - } - - //! - //! \brief Construct a Dims4 from 4 elements. - //! - //! \param d0 The first element. - //! \param d1 The second element. - //! \param d2 The third element. - //! \param d3 The fourth element. - //! - Dims4(int32_t d0, int32_t d1, int32_t d2, int32_t d3) - { - nbDims = 4; - d[0] = d0; - d[1] = d1; - d[2] = d2; - d[3] = d3; - } -}; - -//! -//! \class DimsNCHW -//! \brief Descriptor for data with one index dimension, one channel dimension and two spatial dimensions. -//! -//! \deprecated DimsNCHW will be removed in TensorRT 8.0, use Dims4 instead. -//! -class TRT_DEPRECATED DimsNCHW : public Dims4 -{ -public: - //! - //! \brief Construct an empty DimsNCHW object. - //! - DimsNCHW() - : Dims4() - { - type[0] = DimensionType::kINDEX; - type[1] = DimensionType::kCHANNEL; - type[2] = type[3] = DimensionType::kSPATIAL; - } - - //! - //! \brief Construct a DimsNCHW given batch size, channel count, height and width. - //! - //! \param batchSize The batch size (commonly denoted N). - //! \param channels The channel count. - //! \param height The height of the data. - //! \param width The width of the data. - //! - DimsNCHW(int32_t batchSize, int32_t channels, int32_t height, int32_t width) - : Dims4(batchSize, channels, height, width) - { - type[0] = DimensionType::kINDEX; - type[1] = DimensionType::kCHANNEL; - type[2] = type[3] = DimensionType::kSPATIAL; - } - - //! - //! \brief Get the index count. - //! - //! \return The index count. - //! - int32_t& n() - { - return d[0]; - } - - //! - //! \brief Get the index count. - //! - //! \return The index count. - //! - int32_t n() const - { - return d[0]; - } - - //! - //! \brief Get the channel count. - //! - //! \return The channel count. - //! - int32_t& c() - { - return d[1]; - } - - //! - //! \brief Get the channel count. - //! - //! \return The channel count. - //! - int32_t c() const - { - return d[1]; - } - - //! - //! \brief Get the height. - //! - //! \return The height. - //! - int32_t& h() - { - return d[2]; - } - - //! - //! \brief Get the height. - //! - //! \return The height. - //! - int32_t h() const - { - return d[2]; - } - - //! - //! \brief Get the width. - //! - //! \return The width. - //! - int32_t& w() - { - return d[3]; - } - - //! - //! \brief Get the width. - //! - //! \return The width. - //! - int32_t w() const - { - return d[3]; - } -}; - //! //! \enum LayerType //! @@ -441,38 +66,78 @@ enum class LayerType : int32_t kCONCATENATION = 8, //!< Concatenation layer. kELEMENTWISE = 9, //!< Elementwise layer. kPLUGIN = 10, //!< Plugin layer. - kRNN = 11, //!< RNN layer. - kUNARY = 12, //!< UnaryOp operation Layer. - kPADDING = 13, //!< Padding layer. - kSHUFFLE = 14, //!< Shuffle layer. - kREDUCE = 15, //!< Reduce layer. - kTOPK = 16, //!< TopK layer. - kGATHER = 17, //!< Gather layer. - kMATRIX_MULTIPLY = 18, //!< Matrix multiply layer. - kRAGGED_SOFTMAX = 19, //!< Ragged softmax layer. - kCONSTANT = 20, //!< Constant layer. - kRNN_V2 = 21, //!< RNNv2 layer. - kIDENTITY = 22, //!< Identity layer. - kPLUGIN_V2 = 23, //!< PluginV2 layer. - kSLICE = 24, //!< Slice layer. - kSHAPE = 25, //!< Shape layer. - kPARAMETRIC_RELU = 26, //!< Parametric ReLU layer. - kRESIZE = 27, //!< Resize Layer. - kTRIP_LIMIT = 28, //!< Loop Trip limit layer - kRECURRENCE = 29, //!< Loop Recurrence layer - kITERATOR = 30, //!< Loop Iterator layer - kLOOP_OUTPUT = 31, //!< Loop output layer - kSELECT = 32, //!< Select layer. - kFILL = 33 //!< Fill layer + kUNARY = 11, //!< UnaryOp operation Layer. + kPADDING = 12, //!< Padding layer. + kSHUFFLE = 13, //!< Shuffle layer. + kREDUCE = 14, //!< Reduce layer. + kTOPK = 15, //!< TopK layer. + kGATHER = 16, //!< Gather layer. + kMATRIX_MULTIPLY = 17, //!< Matrix multiply layer. + kRAGGED_SOFTMAX = 18, //!< Ragged softmax layer. + kCONSTANT = 19, //!< Constant layer. + kRNN_V2 = 20, //!< RNNv2 layer. + kIDENTITY = 21, //!< Identity layer. + kPLUGIN_V2 = 22, //!< PluginV2 layer. + kSLICE = 23, //!< Slice layer. + kSHAPE = 24, //!< Shape layer. + kPARAMETRIC_RELU = 25, //!< Parametric ReLU layer. + kRESIZE = 26, //!< Resize Layer. + kTRIP_LIMIT = 27, //!< Loop Trip limit layer + kRECURRENCE = 28, //!< Loop Recurrence layer + kITERATOR = 29, //!< Loop Iterator layer + kLOOP_OUTPUT = 30, //!< Loop output layer + kSELECT = 31, //!< Select layer. + kFILL = 32, //!< Fill layer + kQUANTIZE = 33, //!< Quantize layer + kDEQUANTIZE = 34, //!< Dequantize layer }; //! Maximum number of elements in LayerType enum. \see LayerType template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { - return 34; + return 35; } +//! +//! \brief It is capable of representing one or more TensorFormat by binary OR +//! operations, e.g., 1U << TensorFormat::kCHW4 | 1U << TensorFormat::kCHW32. +//! +//! \see ITensor::getAllowedFormats(), ITensor::setAllowedFormats(), +//! +using TensorFormats = uint32_t; + +//! +//! \enum ActivationType +//! +//! \brief Enumerates the types of activation to perform in an activation layer. +//! +enum class ActivationType : int32_t +{ + kRELU = 0, //!< Rectified linear activation. + kSIGMOID = 1, //!< Sigmoid activation. + kTANH = 2, //!< TanH activation. + kLEAKY_RELU = 3, //!< LeakyRelu activation: x>=0 ? x : alpha * x. + kELU = 4, //!< Elu activation: x>=0 ? x : alpha * (exp(x) - 1). + kSELU = 5, //!< Selu activation: x>0 ? beta * x : beta * (alpha*exp(x) - alpha) + kSOFTSIGN = 6, //!< Softsign activation: x / (1+|x|) + kSOFTPLUS = 7, //!< Parametric softplus activation: alpha*log(exp(beta*x)+1) + kCLIP = 8, //!< Clip activation: max(alpha, min(beta, x)) + kHARD_SIGMOID = 9, //!< Hard sigmoid activation: max(0, min(1, alpha*x+beta)) + kSCALED_TANH = 10, //!< Scaled tanh activation: alpha*tanh(beta*x) + kTHRESHOLDED_RELU = 11 //!< Thresholded ReLU activation: x>alpha ? x : 0 +}; + +namespace impl +{ +//! Maximum number of elements in ActivationType enum. \see ActivationType +template <> +struct EnumMaxImpl +{ + static constexpr int32_t kVALUE = 12; +}; +} // namespace impl + //! //! \class ITensor //! @@ -486,7 +151,7 @@ constexpr inline int32_t EnumMax() //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. //! -class ITensor +class ITensor : public INoCopy { public: //! @@ -501,7 +166,10 @@ public: //! //! \see getName() //! - virtual void setName(const char* name) TRTNOEXCEPT = 0; + void setName(const char* name) noexcept + { + mImpl->setName(name); + } //! //! \brief Get the tensor name. @@ -510,14 +178,17 @@ public: //! //! \see setName() //! - virtual const char* getName() const TRTNOEXCEPT = 0; + const char* getName() const noexcept + { + return mImpl->getName(); + } //! //! \brief Set the dimensions of a tensor. //! - //! For a network input the name is assigned by the application. For a network output it is computed based on - //! the layer parameters and the inputs to the layer. If a tensor size or a parameter is modified in the network, - //! the dimensions of all dependent tensors will be recomputed. + //! For a network input, the dimensions are assigned by the application. For a network output, the dimensions are + //! computed based on the layer parameters and the inputs to the layer. If a tensor size or a parameter is modified + //! in the network, the dimensions of all dependent tensors will be recomputed. //! //! This call is only legal for network input tensors, since the dimensions of layer output tensors are inferred //! based on layer inputs and parameters. @@ -526,7 +197,10 @@ public: //! //! \see getDimensions() //! - virtual void setDimensions(Dims dimensions) TRTNOEXCEPT = 0; // only valid for input tensors + void setDimensions(Dims dimensions) noexcept + { + mImpl->setDimensions(dimensions); + } //! //! \brief Get the dimensions of a tensor. @@ -536,7 +210,10 @@ public: //! \warning getDimensions() returns a -1 for dimensions that are derived from a wildcard dimension. //! \see setDimensions() //! - virtual Dims getDimensions() const TRTNOEXCEPT = 0; + Dims getDimensions() const noexcept + { + return mImpl->getDimensions(); + } //! //! \brief Set the data type of a tensor. @@ -548,7 +225,10 @@ public: //! //! \see getType() //! - virtual void setType(DataType type) TRTNOEXCEPT = 0; + void setType(DataType type) noexcept + { + mImpl->setType(type); + } //! //! \brief Get the data type of a tensor. @@ -557,7 +237,10 @@ public: //! //! \see setType() //! - virtual DataType getType() const TRTNOEXCEPT = 0; + DataType getType() const noexcept + { + return mImpl->getType(); + } //! //! \brief Set dynamic range for the tensor @@ -569,32 +252,27 @@ public: //! //! Requires that min and max be finite, and min <= max. //! - virtual bool setDynamicRange(float min, float max) TRTNOEXCEPT = 0; - - //! - //! \brief Get dynamic range for the tensor - //! - //! \return maximal absolute value of the dynamic range, -1.0f if no dynamic range is set. - //! - //! \deprecated This interface is superseded by getDynamicRangeMin and getDynamicRangeMax and will be removed in - //! TensorRT 8.0. - //! - TRT_DEPRECATED virtual float getDynamicRange() const TRTNOEXCEPT = 0; + bool setDynamicRange(float min, float max) noexcept + { + return mImpl->setDynamicRange(min, max); + } //! //! \brief Whether the tensor is a network input. //! - virtual bool isNetworkInput() const TRTNOEXCEPT = 0; + bool isNetworkInput() const noexcept + { + return mImpl->isNetworkInput(); + } //! //! \brief Whether the tensor is a network output. //! - virtual bool isNetworkOutput() const TRTNOEXCEPT = 0; + bool isNetworkOutput() const noexcept + { + return mImpl->isNetworkOutput(); + } -protected: - virtual ~ITensor() {} - -public: //! //! \brief Set whether to enable broadcast of tensor across the batch. //! @@ -612,7 +290,10 @@ public: //! //! \see getBroadcastAcrossBatch() //! - virtual void setBroadcastAcrossBatch(bool broadcastAcrossBatch) TRTNOEXCEPT = 0; + void setBroadcastAcrossBatch(bool broadcastAcrossBatch) noexcept + { + mImpl->setBroadcastAcrossBatch(broadcastAcrossBatch); + } //! //! \brief Check if tensor is broadcast across the batch. @@ -625,14 +306,20 @@ public: //! //! \see setBroadcastAcrossBatch() //! - virtual bool getBroadcastAcrossBatch() const TRTNOEXCEPT = 0; + bool getBroadcastAcrossBatch() const noexcept + { + return mImpl->getBroadcastAcrossBatch(); + } //! //! \brief Get the storage location of a tensor. //! \return The location of tensor data. //! \see setLocation() //! - virtual TensorLocation getLocation() const TRTNOEXCEPT = 0; + TensorLocation getLocation() const noexcept + { + return mImpl->getLocation(); + } //! //! \brief Set the storage location of a tensor @@ -644,33 +331,48 @@ public: //! //! \see getLocation() //! - virtual void setLocation(TensorLocation location) TRTNOEXCEPT = 0; + void setLocation(TensorLocation location) noexcept + { + mImpl->setLocation(location); + } //! //! \brief Query whether dynamic range is set. //! //! \return True if dynamic range is set, false otherwise. //! - virtual bool dynamicRangeIsSet() const TRTNOEXCEPT = 0; + bool dynamicRangeIsSet() const noexcept + { + return mImpl->dynamicRangeIsSet(); + } //! //! \brief Undo effect of setDynamicRange. //! - virtual void resetDynamicRange() TRTNOEXCEPT = 0; + void resetDynamicRange() noexcept + { + mImpl->resetDynamicRange(); + } //! //! \brief Get minimum of dynamic range. //! //! \return Minimum of dynamic range, or quiet NaN if range was not set. //! - virtual float getDynamicRangeMin() const TRTNOEXCEPT = 0; + float getDynamicRangeMin() const noexcept + { + return mImpl->getDynamicRangeMin(); + } //! //! \brief Get maximum of dynamic range. //! //! \return Maximum of dynamic range, or quiet NaN if range was not set. //! - virtual float getDynamicRangeMax() const TRTNOEXCEPT = 0; + float getDynamicRangeMax() const noexcept + { + return mImpl->getDynamicRangeMax(); + } //! //! \brief Set allowed formats for this tensor. By default all formats are allowed. @@ -686,7 +388,10 @@ public: //! \see ITensor::getAllowedFormats() //! \see TensorFormats //! - virtual void setAllowedFormats(TensorFormats formats) TRTNOEXCEPT = 0; + void setAllowedFormats(TensorFormats formats) noexcept + { + mImpl->setAllowedFormats(formats); + } //! //! \brief Get a bitmask of TensorFormat values that the tensor supports. @@ -696,7 +401,10 @@ public: //! //! \see ITensor::setAllowedFormats() //! - virtual TensorFormats getAllowedFormats() const TRTNOEXCEPT = 0; + TensorFormats getAllowedFormats() const noexcept + { + return mImpl->getAllowedFormats(); + } //! //! \brief Whether the tensor is a shape tensor. @@ -727,7 +435,10 @@ public: //! //! \see INetworkDefinition::markOutputForShapes(), ICudaEngine::isShapeBinding() //! - virtual bool isShapeTensor() const TRTNOEXCEPT = 0; + bool isShapeTensor() const noexcept + { + return mImpl->isShapeTensor(); + } //! //! \brief Whether the tensor is an execution tensor. @@ -747,7 +458,14 @@ public: //! In that case, only its dimensions need to be set at runtime and a nullptr //! can be passed instead of a pointer to its contents. //! - virtual bool isExecutionTensor() const TRTNOEXCEPT = 0; + bool isExecutionTensor() const noexcept + { + return mImpl->isExecutionTensor(); + } + +protected: + apiv::VTensor* mImpl; + virtual ~ITensor() noexcept = default; }; //! @@ -757,7 +475,7 @@ public: //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. //! -class ILayer +class ILayer : public INoCopy { public: //! @@ -765,7 +483,10 @@ public: //! //! \see LayerType //! - virtual LayerType getType() const TRTNOEXCEPT = 0; + LayerType getType() const noexcept + { + return mLayer->getType(); + } //! //! \brief Set the name of a layer. @@ -774,7 +495,10 @@ public: //! //! \see getName() //! - virtual void setName(const char* name) TRTNOEXCEPT = 0; + void setName(const char* name) noexcept + { + mLayer->setName(name); + } //! //! \brief Return the name of a layer. @@ -782,12 +506,18 @@ public: //! \see setName() //! - virtual const char* getName() const TRTNOEXCEPT = 0; + const char* getName() const noexcept + { + return mLayer->getName(); + } //! //! \brief Get the number of inputs of a layer. //! - virtual int32_t getNbInputs() const TRTNOEXCEPT = 0; + int32_t getNbInputs() const noexcept + { + return mLayer->getNbInputs(); + } //! //! \brief Get the layer input corresponding to the given index. @@ -795,38 +525,48 @@ public: //! \param index The index of the input tensor. //! //! \return The input tensor, or nullptr if the index is out of range or the tensor is optional - //! (\ref ISliceLayer, \ref IRNNLayer and \ref IRNNv2Layer). + //! (\ref ISliceLayer and \ref IRNNv2Layer). //! - virtual ITensor* getInput(int32_t index) const TRTNOEXCEPT = 0; + ITensor* getInput(int32_t index) const noexcept + { + return mLayer->getInput(index); + } //! //! \brief Get the number of outputs of a layer. //! - virtual int32_t getNbOutputs() const TRTNOEXCEPT = 0; + int32_t getNbOutputs() const noexcept + { + return mLayer->getNbOutputs(); + } //! //! \brief Get the layer output corresponding to the given index. //! //! \return The indexed output tensor, or nullptr if the index is out of range or the tensor is optional - //! (\ref IRNNLayer and \ref IRNNv2Layer). + //! (\ref IRNNv2Layer). //! - virtual ITensor* getOutput(int32_t index) const TRTNOEXCEPT = 0; + ITensor* getOutput(int32_t index) const noexcept + { + return mLayer->getOutput(index); + } //! - //! \brief Replace an input of this layer with a specific tensor - //! - //! \param index the index of the input to modify. - //! \param tensor the new input tensor - // - //! Except for IShuffleLayer, ISliceLayer, IResizeLayer and ILoopOutputLayer, this method cannot change the number - //! of inputs to a layer. The index argument must be less than the value of getNbInputs(). - //! - //! See overloaded setInput() comments for the layers special behavior. + //! \brief Replace an input of this layer with a specific tensor. //! //! \param index the index of the input to modify. //! \param tensor the new input tensor //! - virtual void setInput(int32_t index, ITensor& tensor) TRTNOEXCEPT = 0; + //! Except for IFillLayer, ILoopOutputLayer, IResizeLayer, IShuffleLayer, and ISliceLayer, + //! this method cannot change the number of inputs to a layer. The index argument must be + //! less than the value of getNbInputs(). + //! + //! See comments for overloads of setInput() for layers with special behavior. + //! + void setInput(int32_t index, ITensor& tensor) noexcept + { + return mLayer->setInput(index, tensor); + } //! //! \brief Set the computational precision of this layer @@ -839,11 +579,14 @@ public: //! computational precision and layer input type based on performance considerations and the flags specified to the //! builder. //! - //! \param precision the computational precision. + //! \param dataType the computational precision. //! //! \see getPrecision() precisionIsSet() resetPrecision() - - virtual void setPrecision(DataType dataType) TRTNOEXCEPT = 0; + //! + void setPrecision(DataType dataType) noexcept + { + mLayer->setPrecision(dataType); + } //! //! \brief get the computational precision of this layer @@ -851,8 +594,11 @@ public: //! \return the computational precision //! //! \see setPrecision() precisionIsSet() resetPrecision() - - virtual DataType getPrecision() const TRTNOEXCEPT = 0; + //! + DataType getPrecision() const noexcept + { + return mLayer->getPrecision(); + } //! //! \brief whether the computational precision has been set for this layer @@ -860,15 +606,21 @@ public: //! \return whether the computational precision has been explicitly set //! //! \see setPrecision() getPrecision() resetPrecision() - - virtual bool precisionIsSet() const TRTNOEXCEPT = 0; + //! + bool precisionIsSet() const noexcept + { + return mLayer->precisionIsSet(); + } //! //! \brief reset the computational precision for this layer //! //! \see setPrecision() getPrecision() precisionIsSet() - - virtual void resetPrecision() TRTNOEXCEPT = 0; + //! + void resetPrecision() noexcept + { + mLayer->resetPrecision(); + } //! //! \brief Set the output type of this layer @@ -895,8 +647,11 @@ public: //! \param dataType the type of the output //! //! \see getOutputType() outputTypeIsSet() resetOutputType() - - virtual void setOutputType(int32_t index, DataType dataType) TRTNOEXCEPT = 0; + //! + void setOutputType(int32_t index, DataType dataType) noexcept + { + mLayer->setOutputType(index, dataType); + } //! //! \brief get the output type of this layer @@ -906,8 +661,11 @@ public: //! unless the output type is inherently DataType::kINT32. //! //! \see getOutputType() outputTypeIsSet() resetOutputType() - - virtual DataType getOutputType(int32_t index) const TRTNOEXCEPT = 0; + //! + DataType getOutputType(int32_t index) const noexcept + { + return mLayer->getOutputType(index); + } //! //! \brief whether the output type has been set for this layer @@ -916,8 +674,11 @@ public: //! \return whether the output type has been explicitly set //! //! \see setOutputType() getOutputType() resetOutputType() - - virtual bool outputTypeIsSet(int32_t index) const TRTNOEXCEPT = 0; + //! + bool outputTypeIsSet(int32_t index) const noexcept + { + return mLayer->outputTypeIsSet(index); + } //! //! \brief reset the output type for this layer @@ -925,11 +686,15 @@ public: //! \param index the index of the output //! //! \see setOutputType() getOutputType() outputTypeIsSet() - - virtual void resetOutputType(int32_t index) TRTNOEXCEPT = 0; + //! + void resetOutputType(int32_t index) noexcept + { + return mLayer->resetOutputType(index); + } protected: - virtual ~ILayer() {} + virtual ~ILayer() noexcept = default; + apiv::VLayer* mLayer; }; //! @@ -1164,12 +929,15 @@ enum class PaddingMode : int32_t kCAFFE_ROUND_UP = 5 //!< Use CAFFE padding, rounding output size up, uses prePadding value. }; +namespace impl +{ //! Maximum number of elements in PaddingMode enum. \see PaddingMode template <> -constexpr inline int32_t EnumMax() +struct EnumMaxImpl { - return 6; -} + static constexpr int32_t kVALUE = 6; +}; +} // namespace impl //! //! \class IConvolutionLayer @@ -1195,7 +963,10 @@ public: //! //! \deprecated Superseded by setKernelSizeNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual void setKernelSize(DimsHW kernelSize) TRTNOEXCEPT = 0; + TRT_DEPRECATED void setKernelSize(DimsHW kernelSize) noexcept + { + mImpl->setKernelSize(kernelSize); + } //! //! \brief Get the HW kernel size of the convolution. @@ -1204,7 +975,10 @@ public: //! //! \deprecated Superseded by getKernelSizeNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual DimsHW getKernelSize() const TRTNOEXCEPT = 0; + TRT_DEPRECATED DimsHW getKernelSize() const noexcept + { + return mImpl->getKernelSize(); + } //! //! \brief Set the number of output maps for the convolution. @@ -1213,14 +987,20 @@ public: //! //! \see getNbOutputMaps() //! - virtual void setNbOutputMaps(int32_t nbOutputMaps) TRTNOEXCEPT = 0; + void setNbOutputMaps(int32_t nbOutputMaps) noexcept + { + mImpl->setNbOutputMaps(nbOutputMaps); + } //! //! \brief Get the number of output maps for the convolution. //! //! \see setNbOutputMaps() //! - virtual int32_t getNbOutputMaps() const TRTNOEXCEPT = 0; + int32_t getNbOutputMaps() const noexcept + { + return mImpl->getNbOutputMaps(); + } //! //! \brief Get the stride of the convolution. @@ -1233,14 +1013,20 @@ public: //! //! \deprecated Superseded by setStrideNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual void setStride(DimsHW stride) TRTNOEXCEPT = 0; + TRT_DEPRECATED void setStride(DimsHW stride) noexcept + { + mImpl->setStride(stride); + } //! //! \brief Get the stride of the convolution. //! //! \deprecated Superseded by getStrideNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual DimsHW getStride() const TRTNOEXCEPT = 0; + TRT_DEPRECATED DimsHW getStride() const noexcept + { + return mImpl->getStride(); + } //! //! \brief Set the padding of the convolution. @@ -1257,7 +1043,10 @@ public: //! //! \deprecated Superseded by setPaddingNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual void setPadding(DimsHW padding) TRTNOEXCEPT = 0; + TRT_DEPRECATED void setPadding(DimsHW padding) noexcept + { + return mImpl->setPadding(padding); + } //! //! \brief Get the padding of the convolution. If the padding is asymmetric, the pre-padding is returned. @@ -1266,7 +1055,10 @@ public: //! //! \deprecated Superseded by getPaddingNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual DimsHW getPadding() const TRTNOEXCEPT = 0; + TRT_DEPRECATED DimsHW getPadding() const noexcept + { + return mImpl->getPadding(); + } //! //! \brief Set the number of groups for a convolution. @@ -1283,14 +1075,20 @@ public: //! //! \see getNbGroups() //! - virtual void setNbGroups(int32_t nbGroups) TRTNOEXCEPT = 0; + void setNbGroups(int32_t nbGroups) noexcept + { + mImpl->setNbGroups(nbGroups); + } //! //! \brief Get the number of groups of the convolution. //! //! \see setNbGroups() //! - virtual int32_t getNbGroups() const TRTNOEXCEPT = 0; + int32_t getNbGroups() const noexcept + { + return mImpl->getNbGroups(); + } //! //! \brief Set the kernel weights for the convolution. @@ -1301,14 +1099,20 @@ public: //! //! \see getKernelWeights() //! - virtual void setKernelWeights(Weights weights) TRTNOEXCEPT = 0; + void setKernelWeights(Weights weights) noexcept + { + mImpl->setKernelWeights(weights); + } //! //! \brief Get the kernel weights of the convolution. //! //! \see setKernelWeights() //! - virtual Weights getKernelWeights() const TRTNOEXCEPT = 0; + Weights getKernelWeights() const noexcept + { + return mImpl->getKernelWeights(); + } //! //! \brief Set the bias weights for the convolution. @@ -1320,14 +1124,20 @@ public: //! //! \see getBiasWeights() //! - virtual void setBiasWeights(Weights weights) TRTNOEXCEPT = 0; + void setBiasWeights(Weights weights) noexcept + { + mImpl->setBiasWeights(weights); + } //! //! \brief Get the bias weights for the convolution. //! //! \see setBiasWeights() //! - virtual Weights getBiasWeights() const TRTNOEXCEPT = 0; + Weights getBiasWeights() const noexcept + { + return mImpl->getBiasWeights(); + } //! //! \brief Set the dilation for a convolution. @@ -1340,7 +1150,10 @@ public: //! //! \deprecated Superseded by setDilationNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual void setDilation(DimsHW dilation) TRTNOEXCEPT = 0; + TRT_DEPRECATED void setDilation(DimsHW dilation) noexcept + { + return mImpl->setDilation(dilation); + } //! //! \brief Get the dilation for a convolution. @@ -1349,12 +1162,11 @@ public: //! //! \deprecated Superseded by getDilationNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual DimsHW getDilation() const TRTNOEXCEPT = 0; + TRT_DEPRECATED DimsHW getDilation() const noexcept + { + return mImpl->getDilation(); + } -protected: - virtual ~IConvolutionLayer() {} - -public: //! //! \brief Set the multi-dimension pre-padding of the convolution. //! @@ -1367,14 +1179,20 @@ public: //! //! \see getPrePadding() //! - virtual void setPrePadding(Dims padding) TRTNOEXCEPT = 0; + void setPrePadding(Dims padding) noexcept + { + mImpl->setPrePadding(padding); + } //! //! \brief Get the pre-padding. //! //! \see setPrePadding() //! - virtual Dims getPrePadding() const TRTNOEXCEPT = 0; + Dims getPrePadding() const noexcept + { + return mImpl->getPrePadding(); + } //! //! \brief Set the multi-dimension post-padding of the convolution. @@ -1388,14 +1206,20 @@ public: //! //! \see getPostPadding() //! - virtual void setPostPadding(Dims padding) TRTNOEXCEPT = 0; + void setPostPadding(Dims padding) noexcept + { + mImpl->setPostPadding(padding); + } //! //! \brief Get the post-padding. //! //! \see setPostPadding() //! - virtual Dims getPostPadding() const TRTNOEXCEPT = 0; + Dims getPostPadding() const noexcept + { + return mImpl->getPostPadding(); + } //! //! \brief Set the padding mode. @@ -1406,7 +1230,10 @@ public: //! //! \see getPaddingMode() //! - virtual void setPaddingMode(PaddingMode paddingMode) TRTNOEXCEPT = 0; + void setPaddingMode(PaddingMode paddingMode) noexcept + { + mImpl->setPaddingMode(paddingMode); + } //! //! \brief Get the padding mode. @@ -1415,7 +1242,10 @@ public: //! //! \see setPaddingMode() //! - virtual PaddingMode getPaddingMode() const TRTNOEXCEPT = 0; + PaddingMode getPaddingMode() const noexcept + { + return mImpl->getPaddingMode(); + } //! //! \brief Set the multi-dimension kernel size of the convolution. @@ -1425,32 +1255,45 @@ public: //! //! \see getKernelSizeNd() //! - virtual void setKernelSizeNd(Dims kernelSize) TRTNOEXCEPT = 0; + void setKernelSizeNd(Dims kernelSize) noexcept + { + mImpl->setKernelSizeNd(kernelSize); + } //! //! \brief Get the multi-dimension kernel size of the convolution. //! //! \see setKernelSizeNd() //! - virtual Dims getKernelSizeNd() const TRTNOEXCEPT = 0; + Dims getKernelSizeNd() const noexcept + { + return mImpl->getKernelSizeNd(); + } //! //! \brief Set the multi-dimension stride of the convolution. //! //! Default: (1, 1, ..., 1) //! - //! If executing this layer on DLA, only support 2D stride, both height and width of stride must be in the range [1,8]. + //! If executing this layer on DLA, only support 2D stride, both height and width of stride must be in the range + //! [1,8]. //! //! \see getStrideNd() setStride() getStride() //! - virtual void setStrideNd(Dims stride) TRTNOEXCEPT = 0; + void setStrideNd(Dims stride) noexcept + { + mImpl->setStrideNd(stride); + } //! //! \brief Get the multi-dimension stride of the convolution. //! //! \see setStrideNd() //! - virtual Dims getStrideNd() const TRTNOEXCEPT = 0; + Dims getStrideNd() const noexcept + { + return mImpl->getStrideNd(); + } //! //! \brief Set the multi-dimension padding of the convolution. @@ -1465,7 +1308,10 @@ public: //! //! \see getPaddingNd() setPadding() getPadding() //! - virtual void setPaddingNd(Dims padding) TRTNOEXCEPT = 0; + void setPaddingNd(Dims padding) noexcept + { + mImpl->setPaddingNd(padding); + } //! //! \brief Get the multi-dimension padding of the convolution. @@ -1474,7 +1320,10 @@ public: //! //! \see setPaddingNd() //! - virtual Dims getPaddingNd() const TRTNOEXCEPT = 0; + Dims getPaddingNd() const noexcept + { + return mImpl->getPaddingNd(); + } //! //! \brief Set the multi-dimension dilation of the convolution. @@ -1485,14 +1334,20 @@ public: //! //! \see getDilation() //! - virtual void setDilationNd(Dims dilation) TRTNOEXCEPT = 0; + void setDilationNd(Dims dilation) noexcept + { + mImpl->setDilationNd(dilation); + } //! //! \brief Get the multi-dimension dilation of the convolution. //! //! \see setDilation() //! - virtual Dims getDilationNd() const TRTNOEXCEPT = 0; + Dims getDilationNd() const noexcept + { + return mImpl->getDilationNd(); + } //! //! \brief Append or replace an input of this layer with a specific tensor @@ -1512,7 +1367,11 @@ public: //! - 1: The kernel weights tensor (a constant tensor). //! //! If this function is called with a value greater than 0, then the function getNbInputs() changes - void setInput(int32_t index, ITensor& tensor) _TENSORRT_OVERRIDE TRTNOEXCEPT = 0; + using ILayer::setInput; + +protected: + virtual ~IConvolutionLayer() noexcept = default; + apiv::VConvolutionLayer* mImpl; }; //! \class IFullyConnectedLayer @@ -1554,28 +1413,40 @@ public: //! //! \see getNbOutputChannels() //! - virtual void setNbOutputChannels(int32_t nbOutputs) TRTNOEXCEPT = 0; + void setNbOutputChannels(int32_t nbOutputs) noexcept + { + mImpl->setNbOutputChannels(nbOutputs); + } //! //! \brief Get the number of output channels `K` from the fully connected layer. //! //! \see setNbOutputChannels() //! - virtual int32_t getNbOutputChannels() const TRTNOEXCEPT = 0; + int32_t getNbOutputChannels() const noexcept + { + return mImpl->getNbOutputChannels(); + } //! //! \brief Set the kernel weights, given as a `KxC` matrix in row-major order. //! //! \see getKernelWeights() //! - virtual void setKernelWeights(Weights weights) TRTNOEXCEPT = 0; + void setKernelWeights(Weights weights) noexcept + { + mImpl->setKernelWeights(weights); + } //! //! \brief Get the kernel weights. //! //! \see setKernelWeights() //! - virtual Weights getKernelWeights() const TRTNOEXCEPT = 0; + Weights getKernelWeights() const noexcept + { + return mImpl->getKernelWeights(); + } //! //! \brief Set the bias weights. @@ -1584,19 +1455,21 @@ public: //! //! \see getBiasWeightsWeights() //! - virtual void setBiasWeights(Weights weights) TRTNOEXCEPT = 0; + void setBiasWeights(Weights weights) noexcept + { + mImpl->setBiasWeights(weights); + } //! //! \brief Get the bias weights. //! //! \see setBiasWeightsWeights() //! - virtual Weights getBiasWeights() const TRTNOEXCEPT = 0; + Weights getBiasWeights() const noexcept + { + return mImpl->getBiasWeights(); + } -protected: - virtual ~IFullyConnectedLayer() {} - -public: //! //! \brief Append or replace an input of this layer with a specific tensor //! @@ -1613,7 +1486,11 @@ public: //! - 1: The kernel weights tensor (a constant tensor). //! //! If this function is called with a value greater than 0, then the function getNbInputs() changes - void setInput(int32_t index, ITensor& tensor) _TENSORRT_OVERRIDE TRTNOEXCEPT = 0; + using ILayer::setInput; + +protected: + virtual ~IFullyConnectedLayer() noexcept = default; + apiv::VFullyConnectedLayer* mImpl; }; //! @@ -1637,18 +1514,21 @@ public: //! //! \see getActivationType(), ActivationType //! - virtual void setActivationType(ActivationType type) TRTNOEXCEPT = 0; + void setActivationType(ActivationType type) noexcept + { + mImpl->setActivationType(type); + } //! //! \brief Get the type of activation to be performed. //! //! \see setActivationType(), ActivationType //! - virtual ActivationType getActivationType() const TRTNOEXCEPT = 0; + ActivationType getActivationType() const noexcept + { + return mImpl->getActivationType(); + } -protected: - virtual ~IActivationLayer() {} -public: //! //! \brief Set the alpha parameter (must be finite). //! @@ -1659,7 +1539,10 @@ public: //! It is ignored by the other activations. //! //! \see getAlpha(), setBeta() - virtual void setAlpha(float alpha) TRTNOEXCEPT = 0; + void setAlpha(float alpha) noexcept + { + mImpl->setAlpha(alpha); + } //! //! \brief Set the beta parameter (must be finite). @@ -1670,19 +1553,32 @@ public: //! It is ignored by the other activations. //! //! \see getBeta(), setAlpha() - virtual void setBeta(float beta) TRTNOEXCEPT = 0; + void setBeta(float beta) noexcept + { + mImpl->setBeta(beta); + } //! //! \brief Get the alpha parameter. //! //! \see getBeta(), setAlpha() - virtual float getAlpha() const TRTNOEXCEPT = 0; + float getAlpha() const noexcept + { + return mImpl->getAlpha(); + } //! //! \brief Get the beta parameter. //! //! \see getAlpha(), setBeta() - virtual float getBeta() const TRTNOEXCEPT = 0; + float getBeta() const noexcept + { + return mImpl->getBeta(); + } + +protected: + virtual ~IActivationLayer() noexcept = default; + apiv::VActivationLayer* mImpl; }; //! @@ -1697,12 +1593,15 @@ enum class PoolingType : int32_t kMAX_AVERAGE_BLEND = 2 // Blending between max and average pooling: (1-blendFactor)*maxPool + blendFactor*avgPool }; +namespace impl +{ //! Maximum number of elements in PoolingType enum. \see PoolingType template <> -constexpr inline int32_t EnumMax() +struct EnumMaxImpl { - return 3; -} + static constexpr int32_t kVALUE = 3; +}; +} // namespace impl //! \class IPoolingLayer //! @@ -1725,14 +1624,20 @@ public: //! //! \see getPoolingType(), PoolingType //! - virtual void setPoolingType(PoolingType type) TRTNOEXCEPT = 0; + void setPoolingType(PoolingType type) noexcept + { + mImpl->setPoolingType(type); + } //! //! \brief Get the type of activation to be performed. //! //! \see setPoolingType(), PoolingType //! - virtual PoolingType getPoolingType() const TRTNOEXCEPT = 0; + PoolingType getPoolingType() const noexcept + { + return mImpl->getPoolingType(); + } //! //! \brief Set the window size for pooling. @@ -1743,7 +1648,10 @@ public: //! //! \deprecated Superseded by setWindowSizeNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual void setWindowSize(DimsHW windowSize) TRTNOEXCEPT = 0; + TRT_DEPRECATED void setWindowSize(DimsHW windowSize) noexcept + { + mImpl->setWindowSize(windowSize); + } //! //! \brief Get the window size for pooling. @@ -1752,7 +1660,10 @@ public: //! //! \deprecated Superseded by getWindowSizeNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual DimsHW getWindowSize() const TRTNOEXCEPT = 0; + TRT_DEPRECATED DimsHW getWindowSize() const noexcept + { + return mImpl->getWindowSize(); + } //! //! \brief Set the stride for pooling. @@ -1765,7 +1676,10 @@ public: //! //! \deprecated Superseded by setStrideNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual void setStride(DimsHW stride) TRTNOEXCEPT = 0; + TRT_DEPRECATED void setStride(DimsHW stride) noexcept + { + mImpl->setStride(stride); + } //! //! \brief Get the stride for pooling. @@ -1774,7 +1688,10 @@ public: //! //! \deprecated Superseded by getStrideNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual DimsHW getStride() const TRTNOEXCEPT = 0; + TRT_DEPRECATED DimsHW getStride() const noexcept + { + return mImpl->getStride(); + } //! //! \brief Set the padding for pooling. @@ -1787,7 +1704,10 @@ public: //! //! \deprecated Superseded by setPaddingNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual void setPadding(DimsHW padding) TRTNOEXCEPT = 0; + TRT_DEPRECATED void setPadding(DimsHW padding) noexcept + { + mImpl->setPadding(padding); + } //! //! \brief Get the padding for pooling. @@ -1798,7 +1718,10 @@ public: //! //! \deprecated Superseded by getPaddingNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual DimsHW getPadding() const TRTNOEXCEPT = 0; + TRT_DEPRECATED DimsHW getPadding() const noexcept + { + return mImpl->getPadding(); + } //! //! \brief Set the blending factor for the max_average_blend mode: @@ -1810,7 +1733,10 @@ public: //! //! \see getBlendFactor() //! - virtual void setBlendFactor(float blendFactor) TRTNOEXCEPT = 0; + void setBlendFactor(float blendFactor) noexcept + { + mImpl->setBlendFactor(blendFactor); + } //! //! \brief Get the blending factor for the max_average_blend mode: @@ -1820,35 +1746,39 @@ public: //! //! \see setBlendFactor() //! - virtual float getBlendFactor() const TRTNOEXCEPT = 0; + float getBlendFactor() const noexcept + { + return mImpl->getBlendFactor(); + } //! //! \brief Set whether average pooling uses as a denominator the overlap area between the window //! and the unpadded input. //! If this is not set, the denominator is the overlap between the pooling window and the padded input. //! - //! If executing this layer on the DLA, only inclusive padding is supported. - //! //! Default: true //! - //! If executing this layer on the DLA, this is ignored as the DLA does not support exclusive padding. + //! \note DLA supports only inclusive padding, and thus when executing this layer on DLA, this must be explicitly + //! set to false. //! //! \see getAverageCountExcludesPadding() //! - virtual void setAverageCountExcludesPadding(bool exclusive) TRTNOEXCEPT = 0; + void setAverageCountExcludesPadding(bool exclusive) noexcept + { + mImpl->setAverageCountExcludesPadding(exclusive); + } //! - //! \brief Get whether exclusive pooling uses as a denominator the overlap area betwen the window + //! \brief Get whether average pooling uses as a denominator the overlap area between the window //! and the unpadded input. //! //! \see setAverageCountExcludesPadding() //! - virtual bool getAverageCountExcludesPadding() const TRTNOEXCEPT = 0; + bool getAverageCountExcludesPadding() const noexcept + { + return mImpl->getAverageCountExcludesPadding(); + } -protected: - virtual ~IPoolingLayer() {} - -public: //! //! \brief Set the multi-dimension pre-padding for pooling. //! @@ -1857,18 +1787,25 @@ public: //! //! Default: (0, 0, ..., 0) //! - //! If executing this layer on DLA, only support 2D padding, both height and width of padding must be in the range [0,7]. + //! If executing this layer on DLA, only support 2D padding, both height and width of padding must be in the range + //! [0,7]. //! //! \see getPrePadding() //! - virtual void setPrePadding(Dims padding) TRTNOEXCEPT = 0; + void setPrePadding(Dims padding) noexcept + { + mImpl->setPrePadding(padding); + } //! //! \brief Get the pre-padding. //! //! \see setPrePadding() //! - virtual Dims getPrePadding() const TRTNOEXCEPT = 0; + Dims getPrePadding() const noexcept + { + return mImpl->getPrePadding(); + } //! //! \brief Set the multi-dimension post-padding for pooling. @@ -1878,18 +1815,25 @@ public: //! //! Default: (0, 0, ..., 0) //! - //! If executing this layer on DLA, only support 2D padding, both height and width of padding must be in the range [0,7]. + //! If executing this layer on DLA, only support 2D padding, both height and width of padding must be in the range + //! [0,7]. //! //! \see getPostPadding() //! - virtual void setPostPadding(Dims padding) TRTNOEXCEPT = 0; + void setPostPadding(Dims padding) noexcept + { + mImpl->setPostPadding(padding); + } //! //! \brief Get the padding. //! - //! \see setPadding() + //! \see setPostPadding() //! - virtual Dims getPostPadding() const TRTNOEXCEPT = 0; + Dims getPostPadding() const noexcept + { + return mImpl->getPostPadding(); + } //! //! \brief Set the padding mode. @@ -1899,7 +1843,10 @@ public: //! Default: kEXPLICIT_ROUND_DOWN //! //! \see getPaddingMode() - virtual void setPaddingMode(PaddingMode paddingMode) TRTNOEXCEPT = 0; + void setPaddingMode(PaddingMode paddingMode) noexcept + { + mImpl->setPaddingMode(paddingMode); + } //! //! \brief Get the padding mode. @@ -1907,41 +1854,58 @@ public: //! Default: kEXPLICIT_ROUND_DOWN //! //! \see setPaddingMode() - virtual PaddingMode getPaddingMode() const TRTNOEXCEPT = 0; + PaddingMode getPaddingMode() const noexcept + { + return mImpl->getPaddingMode(); + } //! //! \brief Set the multi-dimension window size for pooling. //! - //! If executing this layer on DLA, only support 2D window size, both height and width of window size must be in the range [1,8]. + //! If executing this layer on DLA, only support 2D window size, both height and width of window size must be in the + //! range [1,8]. //! //! \see getWindowSizeNd() setWindowSize() getWindowSize() //! - virtual void setWindowSizeNd(Dims windowSize) TRTNOEXCEPT = 0; + void setWindowSizeNd(Dims windowSize) noexcept + { + mImpl->setWindowSizeNd(windowSize); + } //! //! \brief Get the multi-dimension window size for pooling. //! //! \see setWindowSizeNd() //! - virtual Dims getWindowSizeNd() const TRTNOEXCEPT = 0; + Dims getWindowSizeNd() const noexcept + { + return mImpl->getWindowSizeNd(); + } //! //! \brief Set the multi-dimension stride for pooling. //! //! Default: (1, 1, ..., 1) //! - //! If executing this layer on DLA, only support 2D stride, both height and width of stride must be in the range [1,16]. + //! If executing this layer on DLA, only support 2D stride, both height and width of stride must be in the range + //! [1,16]. //! //! \see getStrideNd() setStride() getStride() //! - virtual void setStrideNd(Dims stride) TRTNOEXCEPT = 0; + void setStrideNd(Dims stride) noexcept + { + mImpl->setStrideNd(stride); + } //! //! \brief Get the multi-dimension stride for pooling. //! //! \see setStrideNd() //! - virtual Dims getStrideNd() const TRTNOEXCEPT = 0; + Dims getStrideNd() const noexcept + { + return mImpl->getStrideNd(); + } //! //! \brief Set the multi-dimension padding for pooling. @@ -1952,11 +1916,15 @@ public: //! //! Default: (0, 0, ..., 0) //! - //! If executing this layer on DLA, only support 2D padding, both height and width of padding must be in the range [0,7]. + //! If executing this layer on DLA, only support 2D padding, both height and width of padding must be in the range + //! [0,7]. //! //! \see getPaddingNd() setPadding() getPadding() //! - virtual void setPaddingNd(Dims padding) TRTNOEXCEPT = 0; + void setPaddingNd(Dims padding) noexcept + { + mImpl->setPaddingNd(padding); + } //! //! \brief Get the multi-dimension padding for pooling. @@ -1965,7 +1933,14 @@ public: //! //! \see setPaddingNd() //! - virtual Dims getPaddingNd() const TRTNOEXCEPT = 0; + Dims getPaddingNd() const noexcept + { + return mImpl->getPaddingNd(); + } + +protected: + virtual ~IPoolingLayer() noexcept = default; + apiv::VPoolingLayer* mImpl; }; //! @@ -1989,14 +1964,20 @@ public: //! //! \see setWindowStride() //! - virtual void setWindowSize(int32_t windowSize) TRTNOEXCEPT = 0; + void setWindowSize(int32_t windowSize) noexcept + { + mImpl->setWindowSize(windowSize); + } //! //! \brief Get the LRN window size. //! //! \see getWindowStride() //! - virtual int32_t getWindowSize() const TRTNOEXCEPT = 0; + int32_t getWindowSize() const noexcept + { + return mImpl->getWindowSize(); + } //! //! \brief Set the LRN alpha value. @@ -2004,14 +1985,20 @@ public: //! The valid range is [-1e20, 1e20]. //! \see getAlpha() //! - virtual void setAlpha(float alpha) TRTNOEXCEPT = 0; + void setAlpha(float alpha) noexcept + { + mImpl->setAlpha(alpha); + } //! //! \brief Get the LRN alpha value. //! //! \see setAlpha() //! - virtual float getAlpha() const TRTNOEXCEPT = 0; + float getAlpha() const noexcept + { + return mImpl->getAlpha(); + } //! //! \brief Set the LRN beta value. @@ -2019,14 +2006,20 @@ public: //! The valid range is [0.01, 1e5f]. //! \see getBeta() //! - virtual void setBeta(float beta) TRTNOEXCEPT = 0; + void setBeta(float beta) noexcept + { + mImpl->setBeta(beta); + } //! //! \brief Get the LRN beta value. //! //! \see setBeta() //! - virtual float getBeta() const TRTNOEXCEPT = 0; + float getBeta() const noexcept + { + return mImpl->getBeta(); + } //! //! \brief Set the LRN K value. @@ -2034,17 +2027,24 @@ public: //! The valid range is [1e-5, 1e10]. //! \see getK() //! - virtual void setK(float k) TRTNOEXCEPT = 0; + void setK(float k) noexcept + { + mImpl->setK(k); + } //! //! \brief Get the LRN K value. //! //! \see setK() //! - virtual float getK() const TRTNOEXCEPT = 0; + float getK() const noexcept + { + return mImpl->getK(); + } protected: - virtual ~ILRNLayer() {} + virtual ~ILRNLayer() noexcept = default; + apiv::VLRNLayer* mImpl; }; //! @@ -2061,7 +2061,7 @@ enum class ScaleMode : int32_t //! Maximum number of elements in ScaleMode enum. \see ScaleMode template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 3; } @@ -2082,7 +2082,11 @@ constexpr inline int32_t EnumMax() //! //! The output size is the same as the input size. //! -//! \note The input tensor for this layer is required to have a minimum of 3 dimensions. +//! \note The input tensor for this layer is required to have a minimum of 3 dimensions in implicit batch mode +//! and a minimum of 4 dimensions in explicit batch mode. +//! +//! A scale layer may be used as an INT8 quantization node in a graph, if the output is constrained to INT8 and +//! the input to FP32. Quantization rounds ties to even, and clamps to [-128, 127]. //! //! \see ScaleMode //! @@ -2096,74 +2100,120 @@ public: //! //! \see getMode() //! - virtual void setMode(ScaleMode mode) TRTNOEXCEPT = 0; + void setMode(ScaleMode mode) noexcept + { + mImpl->setMode(mode); + } //! //! \brief Get the scale mode. //! //! \see setMode() //! - virtual ScaleMode getMode() const TRTNOEXCEPT = 0; + ScaleMode getMode() const noexcept + { + return mImpl->getMode(); + } //! //! \brief Set the shift value. //! //! \see getShift() //! - virtual void setShift(Weights shift) TRTNOEXCEPT = 0; + void setShift(Weights shift) noexcept + { + mImpl->setShift(shift); + } //! //! \brief Get the shift value. //! //! \see setShift() //! - virtual Weights getShift() const TRTNOEXCEPT = 0; + Weights getShift() const noexcept + { + return mImpl->getShift(); + } //! //! \brief Set the scale value. //! //! \see getScale() //! - virtual void setScale(Weights scale) TRTNOEXCEPT = 0; + void setScale(Weights scale) noexcept + { + mImpl->setScale(scale); + } //! //! \brief Get the scale value. //! //! \see setScale() //! - virtual Weights getScale() const TRTNOEXCEPT = 0; + Weights getScale() const noexcept + { + return mImpl->getScale(); + } //! //! \brief Set the power value. //! //! \see getPower() //! - virtual void setPower(Weights power) TRTNOEXCEPT = 0; + void setPower(Weights power) noexcept + { + mImpl->setPower(power); + } //! //! \brief Get the power value. //! //! \see setPower() //! - virtual Weights getPower() const TRTNOEXCEPT = 0; + Weights getPower() const noexcept + { + return mImpl->getPower(); + } -protected: - virtual ~IScaleLayer() {} - -public: //! //! \brief Get the channel axis. //! - //! \return channelAxis parameter passed to addScaleNd() + //! \return channelAxis parameter passed to addScaleNd() or set by setChannelAxis() //! - //! The value is the index of the channel axis in the input tensor's dimensions. All dimensions - //! after the channel axis are assumed to be spatial dimensions, and the only spatial dimensions - //! in the tensor. The number of spatial dimensions is thus getDimensions().nbDims - channelAxis - 1. - //! Supported numbers of spatial dimensions are 2 and 3 for 2d and 3d scale layers respectively. + //! The value is the index of the channel axis in the input tensor's dimensions. + //! Scaling happens along the channel axis when ScaleMode::kCHANNEL is enabled. //! //! \see addScaleNd() //! - virtual int32_t getChannelAxis() const TRTNOEXCEPT = 0; + int32_t getChannelAxis() const noexcept + { + return mImpl->getChannelAxis(); + } + + //! + //! \brief Set the channel axis. + //! + //! The value is the index of the channel axis in the input tensor's dimensions. + //! + //! For ScaleMode::kCHANNEL, there can be distinct scale, shift, and power weights for each channel coordinate. + //! For ScaleMode::kELEMENTWISE, there can be distinct scale, shift, and power weights for each combination of + //! coordinates from the channel axis and axes after it. + //! + //! For example, suppose the input tensor has dimensions [10,20,30,40] and the channel axis is 1. + //! Let [n,c,h,w] denote an input coordinate. + //! For ScaleMode::kCHANNEL, the scale, shift, and power weights are indexed by c. + //! For ScaleMode::kELEMENTWISE, the scale, shift, and power weights are indexed by [c,h,w]. + //! + //! \see addScaleNd() + //! + void setChannelAxis(int32_t channelAxis) noexcept + { + mImpl->setChannelAxis(channelAxis); + } + +protected: + virtual ~IScaleLayer() noexcept = default; + apiv::VScaleLayer* mImpl; }; //! @@ -2179,8 +2229,6 @@ public: //! class ISoftMaxLayer : public ILayer { -protected: - virtual ~ISoftMaxLayer() {} public: //! //! \brief Set the axis along which softmax is computed. Currently, only one axis can be set. @@ -2209,16 +2257,27 @@ public: //! set bit 3 with explicit batch mode. //! //! \param axes The axis along which softmax is computed. - //! Here axes is a bitmap. For example, when doing softmax along axis 0, bit 0 is set to 1, axes = 1 << axis = 1. + //! Here axes is a bitmap. For example, when doing softmax along axis 0, bit 0 is set to 1, axes = 1 << axis + //! = 1. //! - virtual void setAxes(uint32_t axes) TRTNOEXCEPT = 0; + void setAxes(uint32_t axes) noexcept + { + mImpl->setAxes(axes); + } //! //! \brief Get the axis along which softmax occurs. //! //! \see setAxes() //! - virtual uint32_t getAxes() const TRTNOEXCEPT = 0; + uint32_t getAxes() const noexcept + { + return mImpl->getAxes(); + } + +protected: + virtual ~ISoftMaxLayer() noexcept = default; + apiv::VSoftMaxLayer* mImpl; }; //! @@ -2226,17 +2285,15 @@ public: //! //! \brief A concatenation layer in a network definition. //! -//! The output channel size is the sum of the channel sizes of the inputs. -//! The other output sizes are the same as the other input sizes, -//! which must all match. +//! The output dimension along the concatenation axis is the sum of the corresponding input dimensions. +//! Every other output dimension is the same as the corresponding dimension of the inputs. +//! +//! \warning All tensors must have the same dimensions except along the concatenation axis. //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. //! class IConcatenationLayer : public ILayer { -protected: - virtual ~IConcatenationLayer() {} - public: //! //! \brief Set the axis along which concatenation occurs. @@ -2248,14 +2305,24 @@ public: //! //! \param axis The axis along which concatenation occurs. //! - virtual void setAxis(int32_t axis) TRTNOEXCEPT = 0; + void setAxis(int32_t axis) noexcept + { + mImpl->setAxis(axis); + } //! //! \brief Get the axis along which concatenation occurs. //! //! \see setAxis() //! - virtual int32_t getAxis() const TRTNOEXCEPT = 0; + int32_t getAxis() const noexcept + { + return mImpl->getAxis(); + } + +protected: + virtual ~IConcatenationLayer() noexcept = default; + apiv::VConcatenationLayer* mImpl; }; //! @@ -2263,8 +2330,6 @@ public: //! //! \brief A deconvolution layer in a network definition. //! -//! The output size is defined using the formula set by INetworkDefinition::setDeconvolutionOutputDimensionsFormula(). -//! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. //! class IDeconvolutionLayer : public ILayer @@ -2281,7 +2346,10 @@ public: //! //! \deprecated Superseded by setKernelSizeNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual void setKernelSize(DimsHW kernelSize) TRTNOEXCEPT = 0; + TRT_DEPRECATED void setKernelSize(DimsHW kernelSize) noexcept + { + mImpl->setKernelSize(kernelSize); + } //! //! \brief Get the HW kernel size of the deconvolution. @@ -2290,7 +2358,10 @@ public: //! //! \deprecated Superseded by getKernelSizeNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual DimsHW getKernelSize() const TRTNOEXCEPT = 0; + TRT_DEPRECATED DimsHW getKernelSize() const noexcept + { + return mImpl->getKernelSize(); + } //! //! \brief Set the number of output feature maps for the deconvolution. @@ -2299,14 +2370,20 @@ public: //! //! \see getNbOutputMaps() //! - virtual void setNbOutputMaps(int32_t nbOutputMaps) TRTNOEXCEPT = 0; + void setNbOutputMaps(int32_t nbOutputMaps) noexcept + { + mImpl->setNbOutputMaps(nbOutputMaps); + } //! //! \brief Get the number of output feature maps for the deconvolution. //! //! \see setNbOutputMaps() //! - virtual int32_t getNbOutputMaps() const TRTNOEXCEPT = 0; + int32_t getNbOutputMaps() const noexcept + { + return mImpl->getNbOutputMaps(); + } //! //! \brief Get the stride of the deconvolution. @@ -2319,7 +2396,10 @@ public: //! //! \deprecated Superseded by setStrideNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual void setStride(DimsHW stride) TRTNOEXCEPT = 0; + TRT_DEPRECATED void setStride(DimsHW stride) noexcept + { + mImpl->setStride(stride); + } //! //! \brief Get the stride of the deconvolution. @@ -2328,7 +2408,10 @@ public: //! //! \deprecated Superseded by getStrideNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual DimsHW getStride() const TRTNOEXCEPT = 0; + TRT_DEPRECATED DimsHW getStride() const noexcept + { + return mImpl->getStride(); + } //! //! \brief Set the padding of the deconvolution. @@ -2345,7 +2428,10 @@ public: //! //! \deprecated Superseded by setPaddingNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual void setPadding(DimsHW padding) TRTNOEXCEPT = 0; + TRT_DEPRECATED void setPadding(DimsHW padding) noexcept + { + mImpl->setPadding(padding); + } //! //! \brief Get the padding of the deconvolution. @@ -2356,7 +2442,10 @@ public: //! //! \deprecated Superseded by getPaddingNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual DimsHW getPadding() const TRTNOEXCEPT = 0; + TRT_DEPRECATED DimsHW getPadding() const noexcept + { + return mImpl->getPadding(); + } //! //! \brief Set the number of groups for a deconvolution. @@ -2373,14 +2462,20 @@ public: //! //! \see getNbGroups() //! - virtual void setNbGroups(int32_t nbGroups) TRTNOEXCEPT = 0; + void setNbGroups(int32_t nbGroups) noexcept + { + mImpl->setNbGroups(nbGroups); + } //! //! \brief Get the number of groups for a deconvolution. //! //! \see setNbGroups() //! - virtual int32_t getNbGroups() const TRTNOEXCEPT = 0; + int32_t getNbGroups() const noexcept + { + return mImpl->getNbGroups(); + } //! //! \brief Set the kernel weights for the deconvolution. @@ -2391,14 +2486,20 @@ public: //! //! \see getWeights() //! - virtual void setKernelWeights(Weights weights) TRTNOEXCEPT = 0; + void setKernelWeights(Weights weights) noexcept + { + mImpl->setKernelWeights(weights); + } //! //! \brief Get the kernel weights for the deconvolution. //! //! \see setNbGroups() //! - virtual Weights getKernelWeights() const TRTNOEXCEPT = 0; + Weights getKernelWeights() const noexcept + { + return mImpl->getKernelWeights(); + } //! //! \brief Set the bias weights for the deconvolution. @@ -2410,19 +2511,21 @@ public: //! //! \see getBiasWeights() //! - virtual void setBiasWeights(Weights weights) TRTNOEXCEPT = 0; + void setBiasWeights(Weights weights) noexcept + { + mImpl->setBiasWeights(weights); + } //! //! \brief Get the bias weights for the deconvolution. //! //! \see getBiasWeights() //! - virtual Weights getBiasWeights() const TRTNOEXCEPT = 0; + Weights getBiasWeights() const noexcept + { + return mImpl->getBiasWeights(); + } -protected: - virtual ~IDeconvolutionLayer() {} - -public: //! //! \brief Set the multi-dimension pre-padding of the deconvolution. //! @@ -2436,14 +2539,20 @@ public: //! //! \see getPrePadding() //! - virtual void setPrePadding(Dims padding) TRTNOEXCEPT = 0; + void setPrePadding(Dims padding) noexcept + { + mImpl->setPrePadding(padding); + } //! //! \brief Get the pre-padding. //! //! \see setPrePadding() //! - virtual Dims getPrePadding() const TRTNOEXCEPT = 0; + Dims getPrePadding() const noexcept + { + return mImpl->getPrePadding(); + } //! //! \brief Set the multi-dimension post-padding of the deconvolution. @@ -2458,14 +2567,20 @@ public: //! //! \see getPostPadding() //! - virtual void setPostPadding(Dims padding) TRTNOEXCEPT = 0; + void setPostPadding(Dims padding) noexcept + { + mImpl->setPostPadding(padding); + } //! //! \brief Get the padding. //! - //! \see setPadding() + //! \see setPostPadding() //! - virtual Dims getPostPadding() const TRTNOEXCEPT = 0; + Dims getPostPadding() const noexcept + { + return mImpl->getPostPadding(); + } //! //! \brief Set the padding mode. @@ -2476,7 +2591,10 @@ public: //! //! \see getPaddingMode() //! - virtual void setPaddingMode(PaddingMode paddingMode) TRTNOEXCEPT = 0; + void setPaddingMode(PaddingMode paddingMode) noexcept + { + mImpl->setPaddingMode(paddingMode); + } //! //! \brief Get the padding mode. @@ -2485,7 +2603,10 @@ public: //! //! \see setPaddingMode() //! - virtual PaddingMode getPaddingMode() const TRTNOEXCEPT = 0; + PaddingMode getPaddingMode() const noexcept + { + return mImpl->getPaddingMode(); + } //! //! \brief Set the multi-dimension kernel size of the deconvolution. @@ -2495,14 +2616,20 @@ public: //! //! \see getKernelSizeNd() setKernelSize() getKernelSize() //! - virtual void setKernelSizeNd(Dims kernelSize) TRTNOEXCEPT = 0; + void setKernelSizeNd(Dims kernelSize) noexcept + { + mImpl->setKernelSizeNd(kernelSize); + } //! //! \brief Get the multi-dimension kernel size of the deconvolution. //! //! \see setKernelSizeNd() //! - virtual Dims getKernelSizeNd() const TRTNOEXCEPT = 0; + Dims getKernelSizeNd() const noexcept + { + return mImpl->getKernelSizeNd(); + } //! //! \brief Set the multi-dimension stride of the deconvolution. @@ -2514,14 +2641,20 @@ public: //! //! \see getStrideNd() setStride() getStride() //! - virtual void setStrideNd(Dims stride) TRTNOEXCEPT = 0; + void setStrideNd(Dims stride) noexcept + { + mImpl->setStrideNd(stride); + } //! //! \brief Get the multi-dimension stride of the deconvolution. //! //! \see setStrideNd() //! - virtual Dims getStrideNd() const TRTNOEXCEPT = 0; + Dims getStrideNd() const noexcept + { + return mImpl->getStrideNd(); + } //! //! \brief Set the multi-dimension padding of the deconvolution. @@ -2536,7 +2669,10 @@ public: //! //! \see getPaddingNd() setPadding() getPadding() //! - virtual void setPaddingNd(Dims padding) TRTNOEXCEPT = 0; + void setPaddingNd(Dims padding) noexcept + { + mImpl->setPaddingNd(padding); + } //! //! \brief Get the multi-dimension padding of the deconvolution. @@ -2545,7 +2681,10 @@ public: //! //! \see setPaddingNd() //! - virtual Dims getPaddingNd() const TRTNOEXCEPT = 0; + Dims getPaddingNd() const noexcept + { + return mImpl->getPaddingNd(); + } //! //! \brief Append or replace an input of this layer with a specific tensor @@ -2564,7 +2703,7 @@ public: //! //! If this function is called with a value greater than 0, then the function getNbInputs() changes //! - void setInput(int32_t index, ITensor& tensor) _TENSORRT_OVERRIDE TRTNOEXCEPT = 0; + using ILayer::setInput; //! \brief Set the multi-dimension dilation of the deconvolution. //! @@ -2572,14 +2711,24 @@ public: //! //! \see getDilationNd() //! - virtual void setDilationNd(Dims dilation) TRTNOEXCEPT = 0; + void setDilationNd(Dims dilation) noexcept + { + mImpl->setDilationNd(dilation); + } //! //! \brief Get the multi-dimension dilation of the deconvolution. //! //! \see setDilationNd() //! - virtual Dims getDilationNd() const TRTNOEXCEPT = 0; + Dims getDilationNd() const noexcept + { + return mImpl->getDilationNd(); + } + +protected: + virtual ~IDeconvolutionLayer() noexcept = default; + apiv::VDeconvolutionLayer* mImpl; }; //! @@ -2607,12 +2756,15 @@ enum class ElementWiseOperation : int32_t kLESS = 13 //!< Check if element in first tensor is less than corresponding element in second tensor. }; +namespace impl +{ //! Maximum number of elements in ElementWiseOperation enum. \see ElementWiseOperation template <> -constexpr inline int32_t EnumMax() +struct EnumMaxImpl { - return 14; -} + static constexpr int32_t kVALUE = 14; +}; +} // namespace impl //! //! \class IElementWiseLayer @@ -2621,7 +2773,12 @@ constexpr inline int32_t EnumMax() //! //! This layer applies a per-element binary operation between corresponding elements of two tensors. //! -//! The input dimensions of the two input tensors must be equal, and the output tensor is the same size as each input. +//! The input tensors must have the same number of dimensions. For each dimension, their lengths must +//! match, or one of them must be one. In the latter case, the tensor is broadcast along that axis. +//! +//! The output tensor has the same number of dimensions as the inputs. For each output dimension, +//! its length is equal to the lengths of the corresponding input dimensions if they match, +//! otherwise it is equal to the length that is not one. //! //! \warning When running this layer on the DLA with Int8 data type, the dynamic ranges of two input tensors shall be //! equal. If the dynamic ranges are generated using calibrator, the largest value shall be used. @@ -2640,7 +2797,10 @@ public: //! //! \see getBiasWeights() //! - virtual void setOperation(ElementWiseOperation op) TRTNOEXCEPT = 0; + void setOperation(ElementWiseOperation op) noexcept + { + return mImpl->setOperation(op); + } //! //! \brief Get the binary operation for the layer. @@ -2649,10 +2809,14 @@ public: //! //! \see setBiasWeights() //! - virtual ElementWiseOperation getOperation() const TRTNOEXCEPT = 0; + ElementWiseOperation getOperation() const noexcept + { + return mImpl->getOperation(); + } protected: - virtual ~IElementWiseLayer() {} + apiv::VElementWiseLayer* mImpl; + virtual ~IElementWiseLayer() noexcept = default; }; //! @@ -2667,14 +2831,20 @@ public: //! //! \see getGatherAxis() //! - virtual void setGatherAxis(int32_t axis) TRTNOEXCEPT = 0; + void setGatherAxis(int32_t axis) noexcept + { + mImpl->setGatherAxis(axis); + } //! //! \brief Get the axis to gather on. //! //! \see setGatherAxis() //! - virtual int32_t getGatherAxis() const TRTNOEXCEPT = 0; + int32_t getGatherAxis() const noexcept + { + return mImpl->getGatherAxis(); + } //! //! \brief Set the number of leading dimensions of indices tensor to be handled elementwise. @@ -2683,17 +2853,24 @@ public: //! //! \see getNbElementWiseDims() //! - virtual void setNbElementWiseDims(int32_t k) TRTNOEXCEPT = 0; + void setNbElementWiseDims(int32_t k) noexcept + { + mImpl->setNbElementWiseDims(k); + } //! //! \brief Get the number of leading dimensions of indices tensor to be handled elementwise. //! //! \see setNbElementWiseDims() //! - virtual int32_t getNbElementWiseDims() const TRTNOEXCEPT = 0; + int32_t getNbElementWiseDims() const noexcept + { + return mImpl->getNbElementWiseDims(); + } protected: - virtual ~IGatherLayer() {} + apiv::VGatherLayer* mImpl; + virtual ~IGatherLayer() noexcept = default; }; //! @@ -2773,7 +2950,7 @@ protected: //! H[t] := (1 - z[t])*h[t] + z[t]*H[t-1] //! ~~~ //! -//! \see IRNNLayer, IRNNv2Layer +//! \see IRNNv2Layer //! enum class RNNOperation : int32_t { @@ -2785,7 +2962,7 @@ enum class RNNOperation : int32_t //! Maximum number of elements in RNNOperation enum. \see RNNOperation template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 4; } @@ -2795,7 +2972,7 @@ constexpr inline int32_t EnumMax() //! //! \brief Enumerates the RNN direction that may be performed by an RNN layer. //! -//! \see IRNNLayer, IRNNv2Layer +//! \see IRNNv2Layer //! enum class RNNDirection : int32_t { @@ -2805,7 +2982,7 @@ enum class RNNDirection : int32_t //! Maximum number of elements in RNNDirection enum. \see RNNDirection template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 2; } @@ -2823,7 +3000,7 @@ constexpr inline int32_t EnumMax() //! and `W[g]` is conceptually an identity matrix. In this case, the input vector `X[t]` must have length `H` //! (the size of the hidden state). //! -//! \see IRNNLayer, IRNNv2Layer +//! \see IRNNv2Layer //! enum class RNNInputMode : int32_t { @@ -2833,178 +3010,163 @@ enum class RNNInputMode : int32_t //! Maximum number of elements in RNNInputMode enum. \see RNNInputMode template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 2; } //! -//! \class IRNNLayer +//! \enum RNNGateType //! -//! \brief A RNN layer in a network definition. +//! \brief Identifies an individual gate within an RNN cell. //! -//! This layer applies an RNN operation on the inputs. This layer only works with networks that -//! that have an implicit batch dimension. For dynamic shapes and explicit batch dimension networks, -//! use IRNNv2Layer. +//! \see RNNOperation //! -//! \deprecated This interface is superseded by IRNNv2Layer and will be removed in TensorRT 8.0. +enum class RNNGateType : int32_t +{ + kINPUT = 0, //!< Input gate (i). + kOUTPUT = 1, //!< Output gate (o). + kFORGET = 2, //!< Forget gate (f). + kUPDATE = 3, //!< Update gate (z). + kRESET = 4, //!< Reset gate (r). + kCELL = 5, //!< Cell gate (c). + kHIDDEN = 6 //!< Hidden gate (h). +}; + +template <> +constexpr inline int32_t EnumMax() noexcept +{ + return 7; +} //!< Maximum number of elements in RNNGateType enum. \see RNNGateType + +//! +//! \class IRNNv2Layer +//! +//! \brief An RNN layer in a network definition, version 2. +//! +//! This layer supersedes IRNNLayer. +//! +//! \deprecated IRNNv2Layer will be removed in TensorRT 9.0, use INetworkDefinition::addLoop instead. //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. //! -class TRT_DEPRECATED IRNNLayer : public ILayer +class TRT_DEPRECATED IRNNv2Layer : public ILayer { public: - //! - //! \brief Get the number of layers in the RNN. - //! - //! \return The number of layers in the RNN. - //! - virtual uint32_t getLayerCount() const TRTNOEXCEPT = 0; + int32_t getLayerCount() const noexcept + { + return mImpl->getLayerCount(); + } //!< Get the layer count of the RNN. + int32_t getHiddenSize() const noexcept + { + return mImpl->getHiddenSize(); + } //!< Get the hidden size of the RNN. + int32_t getMaxSeqLength() const noexcept + { + return mImpl->getMaxSeqLength(); + } //!< Get the maximum sequence length of the RNN. + int32_t getDataLength() const noexcept + { + return mImpl->getDataLength(); + } //!< Get the maximum data length of the RNN. //! - //! \brief Get the size of the hidden layers. + //! \brief Specify individual sequence lengths in the batch with the ITensor pointed to by + //! \p seqLengths. //! - //! The hidden size is the value of hiddenSize parameter passed into addRNN(). + //! The \p seqLengths ITensor should be a {N1, ..., Np} tensor, where N1..Np are the index dimensions + //! of the input tensor to the RNN. //! - //! \return The internal hidden layer size for the RNN. - //! \see getDirection(), addRNN() + //! If this is not specified, then the RNN layer assumes all sequences are size getMaxSeqLength(). //! - virtual std::size_t getHiddenSize() const TRTNOEXCEPT = 0; + //! All sequence lengths in \p seqLengths should be in the range [1, getMaxSeqLength()]. Zero-length + //! sequences are not supported. + //! + //! This tensor must be of type DataType::kINT32. + //! + void setSequenceLengths(ITensor& seqLengths) noexcept + { + return mImpl->setSequenceLengths(seqLengths); + } //! - //! \brief Get the sequence length. + //! \brief Get the sequence lengths specified for the RNN. //! - //! The sequence length is the maximum number of time steps passed into the addRNN() function. - //! This is also the maximum number of input tensors that the RNN can process at once. + //! \return nullptr if no sequence lengths were specified, the sequence length data otherwise. //! - //! \return the maximum number of time steps that can be executed by a single call RNN layer. + //! \see setSequenceLengths() //! - virtual int32_t getSeqLength() const TRTNOEXCEPT = 0; + ITensor* getSequenceLengths() const noexcept + { + return mImpl->getSequenceLengths(); + } //! //! \brief Set the operation of the RNN layer. - //! //! \see getOperation(), RNNOperation //! - virtual void setOperation(RNNOperation op) TRTNOEXCEPT = 0; + void setOperation(RNNOperation op) noexcept + { + mImpl->setOperation(op); + } //! //! \brief Get the operation of the RNN layer. - //! //! \see setOperation(), RNNOperation //! - virtual RNNOperation getOperation() const TRTNOEXCEPT = 0; + RNNOperation getOperation() const noexcept + { + return mImpl->getOperation(); + } //! - //! \brief Set the operation of the RNN layer. - //! + //! \brief Set the input mode of the RNN layer. //! \see getInputMode(), RNNInputMode //! - virtual void setInputMode(RNNInputMode op) TRTNOEXCEPT = 0; + void setInputMode(RNNInputMode op) noexcept + { + mImpl->setInputMode(op); + } //! - //! \brief Get the operation of the RNN layer. - //! + //! \brief Get the input mode of the RNN layer. //! \see setInputMode(), RNNInputMode //! - virtual RNNInputMode getInputMode() const TRTNOEXCEPT = 0; + RNNInputMode getInputMode() const noexcept + { + return mImpl->getInputMode(); + } //! //! \brief Set the direction of the RNN layer. //! - //! The direction determines if the RNN is run - //! as a unidirectional(left to right) or + //! The direction determines if the RNN is run as a unidirectional(left to right) or //! bidirectional(left to right and right to left). - //! In the ::kBIDIRECTION case the - //! output is concatenated together, resulting + //! In the ::kBIDIRECTION case the output is concatenated together, resulting //! in output size of 2x getHiddenSize(). + //! //! \see getDirection(), RNNDirection //! - virtual void setDirection(RNNDirection op) TRTNOEXCEPT = 0; + void setDirection(RNNDirection op) noexcept + { + mImpl->setDirection(op); + } //! //! \brief Get the direction of the RNN layer. - //! //! \see setDirection(), RNNDirection //! - virtual RNNDirection getDirection() const TRTNOEXCEPT = 0; + RNNDirection getDirection() const noexcept + { + return mImpl->getDirection(); + } //! - //! \param weights The weight structure holding the weight parameters. + //! \brief Set the weight parameters for an individual gate in the RNN. //! - //! \brief Set the weight parameters for the RNN. - //! - //! The trained weights for the weight parameter matrices of the RNN. //! The #DataType for this structure must be ::kFLOAT or ::kHALF, and must be the same //! datatype as the input tensor. //! - //! The layout of the weight structure depends on the #RNNOperation, #RNNInputMode, and - //! #RNNDirection of the layer. The array specified by `weights.values` contains a sequence of - //! parameter matrices, where each parameter matrix is linearly appended after the previous - //! without padding; e.g., if parameter matrix 0 and 1 have M and N elements respectively, then - //! the layout of `weights.values` in memory looks like: - //! - //! ~~~ - //! index | 0 1 2 3 4 ... M-2 M-1 | M M+1 ... M+N-2 M+N-1 | M+N M+N+1 M+N+2 ... | ... - //! data |-- parameter matrix 0 --|-- parameter matrix 1 --|-- parameter matrix 2 --| ... - //! ~~~ - //! - //! The following sections describe \ref setRNNWeightsOrder "the order of weight matrices" and - //! \ref setRNNWeightsLayout "the layout of elements within a weight matrix". - //! - //! \section setRNNWeightsOrder Order of weight matrices - //! - //! The parameter matrices are ordered as described below: - //! - //! ~~~ - //! Let G(op, l) be defined to be a function that produces lists of parameter names, as follows: - //! - //! G(::kRELU, l) := [ Wl[i], Rl[i] ] - //! G(::kTANH, l) := [ Wl[i], Rl[i] ] - //! G(::kLSTM, l) := [ Wl[f], Wl[i], Wl[c], Wl[o], Rl[f], Rl[i], Rl[c], Rl[o] ] - //! G(::kGRU, l) := [ Wl[z], Wl[r], Wl[h], Rl[z], Rl[r], Rl[h] ] - //! - //! where Wl[g] and Rl[g] are the names of the input and recurrent - //! input weight matrices for gate g, layer index l. - //! - //! See RNNOperation for an overview of the naming convention used for gates. - //! - //! If getDirection() == ::kUNIDIRECTION, then l identifies the stacked layer of the - //! RNN, with l=0 being the first recurrent layer and l=L-1 being the last recurrent layer. - //! - //! If getDirection() == ::kBIDIRECTION, then (l % 2) identifies the direction of the - //! recurrent layer (forward if 0, or backward if 1), and (l / 2) identifies the position - //! of the recurrent layer within the (forward or backward) stack. - //! - //! Let op := getOperation(), - //! L := { ::kUNIDIRECTION => getLayerCount() - //! { ::kBIDIRECTION => (2 * getLayerCount()) - //! - //! Then the ordering of parameter matrices is the list produced by concatenating - //! G(op, 0), G(op, 1), G(op, 2), ..., G(op, L-1). - //! ~~~ - //! - //! For example: - //! - //! - an RNN with `getLayerCount() == 3`, `getDirection() == ::kUNIDIRECTION`, - //! and `getOperation() == ::kRELU` has the following order: - //! - //! `[ W0[i], R0[i], W1[i], R1[i], W2[i], R2[i] ]` - //! - //! - an RNN with `getLayerCount() == 2`, `getDirection() == ::kUNIDIRECTION`, - //! and `getOperation() == ::kGRU` has the following order: - //! - //! `[ W0[z], W0[r], W0[h], R0[z], R0[r], R0[h], W1[z], W1[r], W1[h], R1[z], R1[r], R1[h] ]` - //! - //! - an RNN with `getLayerCount() == 2`, `getDirection() == ::kBIDIRECTION`, - //! and `getOperation() == ::kRELU` has the following order: - //! - //! `[ W0_fw[i], R0_fw[i], W0_bw[i], R0_bw[i], W1_fw[i], R1_fw[i], W1_bw[i], R1_bw[i] ]` - //! - //! (fw = "forward", bw = "backward") - //! - //! \section setRNNWeightsLayout Layout of elements within a weight matrix - //! //! Each parameter matrix is row-major in memory, and has the following dimensions: //! //! ~~~ @@ -3041,254 +3203,7 @@ public: //! backward) RNN cell operates on the previous (forward or //! backward) RNN cell's hidden state, which is size `H`). //! - //! \see getWeights(), #RNNOperation - //! - virtual void setWeights(Weights weights) TRTNOEXCEPT = 0; - - //! - //! \brief Get the W weights for the RNN. - //! - //! \see setWeights() - //! - virtual Weights getWeights() const TRTNOEXCEPT = 0; - - //! - //! \param bias The weight structure holding the bias parameters. - //! - //! \brief Set the bias parameters for the RNN. - //! - //! The trained weights for the bias parameter vectors of the RNN. - //! The #DataType for this structure must be ::kFLOAT or ::kHALF, and must be the same - //! datatype as the input tensor. - //! - //! The layout of the weight structure depends on the #RNNOperation, #RNNInputMode, and - //! #RNNDirection of the layer. The array specified by `weights.values` contains a sequence of - //! bias vectors, where each bias vector is linearly appended after the previous - //! without padding; e.g., if bias vector 0 and 1 have M and N elements respectively, then - //! the layout of `weights.values` in memory looks like: - //! - //! ~~~ - //! index | 0 1 2 3 4 ... M-2 M-1 | M M+1 ... M+N-2 M+N-1 | M+N M+N+1 M+N+2 ... | ... - //! data |-- bias vector 0 --|-- bias vector 1 --|-- bias vector 2 --| ... - //! ~~~ - //! - //! The ordering of bias vectors is similar to the \ref setRNNWeightsOrder "ordering of weight matrices" - //! as described in setWeights(). To determine the order of bias vectors for a given RNN configuration, - //! determine the ordered list of weight matrices `[ W0, W1, ..., Wn ]`. Then replace each weight matrix - //! with its corresponding bias vector, i.e. apply the following transform (for layer `l`, gate `g`): - //! - //! - `Wl[g]` becomes `Wbl[g]` - //! - `Rl[g]` becomes `Rbl[g]` - //! - //! For example: - //! - //! - an RNN with `getLayerCount() == 3`, `getDirection() == ::kUNIDIRECTION`, - //! and `getOperation() == ::kRELU` has the following order: - //! - //! `[ Wb0[i], Rb0[i], Wb1[i], Rb1[i], Wb2[i], Rb2[i] ]` - //! - //! - an RNN with `getLayerCount() == 2`, `getDirection() == ::kUNIDIRECTION`, - //! and `getOperation() == ::kGRU` has the following order: - //! - //! `[ Wb0[z], Wb0[r], Wb0[h], Rb0[z], Rb0[r], Rb0[h], Wb1[z], Wb1[r], Wb1[h], Rb1[z], Rb1[r], Rb1[h] ]` - //! - //! - an RNN with `getLayerCount() == 2`, `getDirection() == ::kBIDIRECTION`, - //! and `getOperation() == ::kRELU` has the following order: - //! - //! `[ Wb0_fw[i], Rb0_fw[i], Wb0_bw[i], Rb0_bw[i], Wb1_fw[i], Rb1_fw[i], Wb1_bw[i], Rb1_bw[i] ]` - //! - //! (fw = "forward", bw = "backward") - //! - //! Each bias vector has a fixed size, getHiddenSize(). - //! - //! \see getBias(), #RNNOperation - //! - virtual void setBias(Weights bias) TRTNOEXCEPT = 0; - - //! - //! \brief Get the bias parameter vector for the RNN. - //! - //! \see setBias() - //! - virtual Weights getBias() const TRTNOEXCEPT = 0; - - //! - //! \brief Get the length of the data being processed by the RNN for use in computing - //! other values. - //! - //! \see setHiddenState(), setCellState() - //! - virtual int32_t getDataLength() const TRTNOEXCEPT = 0; - - //! - //! \param hidden The initial hidden state of the RNN. - //! - //! \brief Set the initial hidden state of the RNN with the provided \p hidden ITensor. - //! - //! The layout for \p hidden is a linear layout of a 3D matrix: - //! - C - The number of layers in the RNN, it must match getLayerCount(). - //! - H - The number of mini-batches for each time sequence. - //! - W - The size of the per layer hidden states, it must match getHiddenSize(). - //! - //! If getDirection() is ::kBIDIRECTION, the amount of space required is doubled and C is equal to - //! getLayerCount() * 2. - //! - //! If hidden is not specified, then the initial hidden state is set to zero. - //! - //! \see getHiddenState() - //! - virtual void setHiddenState(ITensor& hidden) TRTNOEXCEPT = 0; - - //! - //! \brief Get the initial hidden state of the RNN. - //! - //! \return nullptr if no initial hidden tensor was specified, the initial hidden data otherwise. - //! - virtual ITensor* getHiddenState() const TRTNOEXCEPT = 0; - - //! - //! \param cell The initial cell state of the RNN. - //! - //! \brief Set the initial cell state of the RNN with the provided \p cell ITensor. - //! - //! The layout for \p cell is a linear layout of a 3D matrix: - //! - C - The number of layers in the RNN, it must match getLayerCount(). - //! - H - The number of mini-batches for each time sequence. - //! - W - The size of the per layer hidden states, it must match getHiddenSize(). - //! - //! If \p cell is not specified, then the initial cell state is set to zero. - //! - //! If getDirection() is ::kBIDIRECTION, the amount of space required is doubled and C is equal to - //! getLayerCount() * 2. - //! - //! The cell state only affects LSTM RNN's. - //! - //! \see getCellState() - //! - virtual void setCellState(ITensor& cell) TRTNOEXCEPT = 0; - - //! - //! \brief Get the initial cell state of the RNN. - //! - //! \return nullptr if no initial cell tensor was specified, the initial cell data otherwise. - //! - virtual ITensor* getCellState() const TRTNOEXCEPT = 0; - -protected: - virtual ~IRNNLayer() {} -}; - -//! -//! \enum RNNGateType -//! -//! \brief Identifies an individual gate within an RNN cell. -//! -//! \see RNNOperation -//! -enum class RNNGateType : int32_t -{ - kINPUT = 0, //!< Input gate (i). - kOUTPUT = 1, //!< Output gate (o). - kFORGET = 2, //!< Forget gate (f). - kUPDATE = 3, //!< Update gate (z). - kRESET = 4, //!< Reset gate (r). - kCELL = 5, //!< Cell gate (c). - kHIDDEN = 6 //!< Hidden gate (h). -}; - -//! Maximum number of elements in RNNGateType enum. \see RNNGateType -template <> -constexpr inline int32_t EnumMax() -{ - return 7; -} - -//! -//! \class IRNNv2Layer -//! -//! \brief An RNN layer in a network definition, version 2. -//! -//! This layer supersedes IRNNLayer. -//! -//! \deprecated IRNNv2Layer will be removed in TensorRT 9.0, use ILoop::addLoop instead. -//! -//! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. -//! -class TRT_DEPRECATED IRNNv2Layer : public ILayer -{ -public: - virtual int32_t getLayerCount() const TRTNOEXCEPT = 0; //!< Get the layer count of the RNN - virtual int32_t getHiddenSize() const TRTNOEXCEPT = 0; //!< Get the hidden size of the RNN - virtual int32_t getMaxSeqLength() const TRTNOEXCEPT = 0; //!< Get the maximum sequence length of the RNN - virtual int32_t getDataLength() const TRTNOEXCEPT = 0; //!< Get the maximum data length of the RNN - - //! - //! \brief Specify individual sequence lengths in the batch with the ITensor pointed to by - //! \p seqLengths. - //! - //! The \p seqLengths ITensor should be a {N1, ..., Np} tensor, where N1..Np are the index dimensions - //! of the input tensor to the RNN. - //! - //! If this is not specified, then the RNN layer assumes all sequences are size getMaxSeqLength(). - //! - //! All sequence lengths in \p seqLengths should be in the range [1, getMaxSeqLength()]. Zero-length - //! sequences are not supported. - //! - //! This tensor must be of type DataType::kINT32. - //! - virtual void setSequenceLengths(ITensor& seqLengths) TRTNOEXCEPT = 0; - - //! - //! \brief Get the sequence lengths specified for the RNN. - //! - //! \return nullptr if no sequence lengths were specified, the sequence length data otherwise. - //! - //! \see setSequenceLengths() - //! - virtual ITensor* getSequenceLengths() const TRTNOEXCEPT = 0; - - //! - //! \brief Set the operation of the RNN layer. - //! \see getOperation(), RNNOperation - //! - virtual void setOperation(RNNOperation op) TRTNOEXCEPT = 0; - - //! - //! \brief Get the operation of the RNN layer. - //! \see setOperation(), RNNOperation - //! - virtual RNNOperation getOperation() const TRTNOEXCEPT = 0; - - //! - //! \brief Set the input mode of the RNN layer. - //! \see getInputMode(), RNNInputMode - //! - virtual void setInputMode(RNNInputMode op) TRTNOEXCEPT = 0; - - //! - //! \brief Get the input mode of the RNN layer. - //! \see setInputMode(), RNNInputMode - //! - virtual RNNInputMode getInputMode() const TRTNOEXCEPT = 0; - - //! - //! \brief Set the direction of the RNN layer. - //! \see getDirection(), RNNDirection - //! - virtual void setDirection(RNNDirection op) TRTNOEXCEPT = 0; - - //! - //! \brief Get the direction of the RNN layer. - //! \see setDirection(), RNNDirection - //! - virtual RNNDirection getDirection() const TRTNOEXCEPT = 0; - - //! - //! \brief Set the weight parameters for an individual gate in the RNN. - //! //! \param layerIndex The index of the layer that contains this gate. See the section - //! \ref setRNNWeightsOrder "Order of weight matrices" in IRNNLayer::setWeights() - //! for a description of the layer index. //! \param gate The name of the gate within the RNN layer. The gate name must correspond //! to one of the gates used by this layer's #RNNOperation. //! \param isW True if the weight parameters are for the input matrix W[g] @@ -3300,17 +3215,28 @@ public: //! in IRNNLayer::setWeights() for documentation on the expected //! dimensions of this matrix. //! - virtual void setWeightsForGate(int32_t layerIndex, RNNGateType gate, bool isW, Weights weights) TRTNOEXCEPT = 0; + void setWeightsForGate(int32_t layerIndex, RNNGateType gate, bool isW, Weights weights) noexcept + { + mImpl->setWeightsForGate(layerIndex, gate, isW, weights); + } //! //! \brief Get the weight parameters for an individual gate in the RNN. //! \see setWeightsForGate() //! - virtual Weights getWeightsForGate(int32_t layerIndex, RNNGateType gate, bool isW) const TRTNOEXCEPT = 0; + Weights getWeightsForGate(int32_t layerIndex, RNNGateType gate, bool isW) const noexcept + { + return mImpl->getWeightsForGate(layerIndex, gate, isW); + } //! //! \brief Set the bias parameters for an individual gate in the RNN. //! + //! The #DataType for this structure must be ::kFLOAT or ::kHALF, and must be the same + //! datatype as the input tensor. + //! + //! Each bias vector has a fixed size, getHiddenSize(). + //! //! \param layerIndex The index of the layer that contains this gate. See the section //! \ref setRNNWeightsOrder "Order of weight matrices" in IRNNLayer::setWeights() //! for a description of the layer index. @@ -3323,13 +3249,19 @@ public: //! \param bias The weight structure holding the bias parameters, which should be an //! array of size getHiddenSize(). //! - virtual void setBiasForGate(int32_t layerIndex, RNNGateType gate, bool isW, Weights bias) TRTNOEXCEPT = 0; + void setBiasForGate(int32_t layerIndex, RNNGateType gate, bool isW, Weights bias) noexcept + { + mImpl->setBiasForGate(layerIndex, gate, isW, bias); + } //! //! \brief Get the bias parameters for an individual gate in the RNN. //! \see setBiasForGate() //! - virtual Weights getBiasForGate(int32_t layerIndex, RNNGateType gate, bool isW) const TRTNOEXCEPT = 0; + Weights getBiasForGate(int32_t layerIndex, RNNGateType gate, bool isW) const noexcept + { + return mImpl->getBiasForGate(layerIndex, gate, isW); + } //! //! \brief Set the initial hidden state of the RNN with the provided \p hidden ITensor. @@ -3343,13 +3275,19 @@ public: //! final backward hidden state is stored in `L= 2*l + 1`. //! - `H` is the hidden state for each layer, equal to getHiddenSize(). //! - virtual void setHiddenState(ITensor& hidden) TRTNOEXCEPT = 0; + void setHiddenState(ITensor& hidden) noexcept + { + mImpl->setHiddenState(hidden); + } //! //! \brief Get the initial hidden state of the RNN. //! \see setHiddenState() //! - virtual ITensor* getHiddenState() const TRTNOEXCEPT = 0; + ITensor* getHiddenState() const noexcept + { + return mImpl->getHiddenState(); + } //! //! \brief Set the initial cell state of the LSTM with the provided \p cell ITensor. @@ -3365,71 +3303,23 @@ public: //! //! It is an error to call setCellState() on an RNN layer that is not configured with RNNOperation::kLSTM. //! - virtual void setCellState(ITensor& cell) TRTNOEXCEPT = 0; + void setCellState(ITensor& cell) noexcept + { + mImpl->setCellState(cell); + } //! //! \brief Get the initial cell state of the RNN. //! \see setCellState() //! - virtual ITensor* getCellState() const TRTNOEXCEPT = 0; + ITensor* getCellState() const noexcept + { + return mImpl->getCellState(); + } protected: - virtual ~IRNNv2Layer() {} -}; - -//! -//! \class IOutputDimensionsFormula -//! -//! \brief Application-implemented interface to compute layer output sizes. -//! -//! \deprecated IOutputDimensionsFormula has been superseded by PaddingMode and will be removed in TensorRT 9.0. -//! -class TRT_DEPRECATED IOutputDimensionsFormula -{ -public: - //! - //! \brief Application-implemented interface to compute the HW output dimensions of a layer from the layer input - //! and parameters. - //! - //! \param inputDims The input dimensions of the layer. - //! \param kernelSize The kernel size (or window size, for a pooling layer) parameter of the layer operation. - //! \param stride The stride parameter for the layer. - //! \param padding The padding parameter of the layer. - //! \param dilation The dilation parameter of the layer (only applicable to convolutions). - //! \param layerName The name of the layer. - //! - //! \return The output size of the layer - //! - //! Note that for dilated convolutions, the dilation is applied to the kernel size before this routine is called. - //! - virtual DimsHW compute(DimsHW inputDims, DimsHW kernelSize, DimsHW stride, DimsHW padding, DimsHW dilation, const char* layerName) const TRTNOEXCEPT = 0; - - virtual ~IOutputDimensionsFormula() {} -}; - -//! -//! \class IPluginLayer -//! -//! \brief Layer type for plugins. -//! -//! \see IPluginExt -//! -//! \deprecated This interface is superseded by IPluginV2Layer and will be removed in TensorRT 8.0. -//! -//! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. -//! -class TRT_DEPRECATED IPluginLayer : public ILayer -{ -public: - //! - //! \brief Get the plugin for the layer. - //! - //! \see IPluginExt - //! - virtual IPlugin& getPlugin() TRTNOEXCEPT = 0; - -protected: - virtual ~IPluginLayer() {} + apiv::VRNNv2Layer* mImpl; + virtual ~IRNNv2Layer() noexcept = default; }; //! @@ -3449,10 +3339,14 @@ public: //! //! \see IPluginV2 //! - virtual IPluginV2& getPlugin() TRTNOEXCEPT = 0; + IPluginV2& getPlugin() noexcept + { + return mImpl->getPlugin(); + } protected: - virtual ~IPluginV2Layer() {} + apiv::VPluginV2Layer* mImpl; + virtual ~IPluginV2Layer() noexcept = default; }; //! @@ -3489,7 +3383,7 @@ enum class UnaryOperation : int32_t //! Maximum number of elements in UnaryOperation enum. \see UnaryOperation template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 21; } @@ -3509,17 +3403,24 @@ public: //! //! \see getOperation(), UnaryOperation //! - virtual void setOperation(UnaryOperation op) TRTNOEXCEPT = 0; + void setOperation(UnaryOperation op) noexcept + { + mImpl->setOperation(op); + } //! //! \brief Get the unary operation for the layer. //! //! \see setOperation(), UnaryOperation //! - virtual UnaryOperation getOperation() const TRTNOEXCEPT = 0; + UnaryOperation getOperation() const noexcept + { + return mImpl->getOperation(); + } protected: - virtual ~IUnaryLayer() {} + apiv::VUnaryLayer* mImpl; + virtual ~IUnaryLayer() noexcept = default; }; //! @@ -3551,7 +3452,7 @@ enum class ReduceOperation : int32_t //! Maximum number of elements in ReduceOperation enum. \see ReduceOperation template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 5; } @@ -3571,45 +3472,64 @@ public: //! //! \see getOperation(), ReduceOperation //! - virtual void setOperation(ReduceOperation op) TRTNOEXCEPT = 0; + void setOperation(ReduceOperation op) noexcept + { + mImpl->setOperation(op); + } //! //! \brief Get the reduce operation for the layer. //! //! \see setOperation(), ReduceOperation //! - virtual ReduceOperation getOperation() const TRTNOEXCEPT = 0; + ReduceOperation getOperation() const noexcept + { + return mImpl->getOperation(); + } //! //! \brief Set the axes over which to reduce. //! //! \see getReduceAxes //! - virtual void setReduceAxes(uint32_t reduceAxes) TRTNOEXCEPT = 0; + void setReduceAxes(uint32_t reduceAxes) noexcept + { + mImpl->setReduceAxes(reduceAxes); + } //! //! \brief Get the axes over which to reduce for the layer. //! //! \see setReduceAxes //! - virtual uint32_t getReduceAxes() const TRTNOEXCEPT = 0; + uint32_t getReduceAxes() const noexcept + { + return mImpl->getReduceAxes(); + } //! //! \brief Set the boolean that specifies whether or not to keep the reduced dimensions for the layer. //! //! \see getKeepDimensions //! - virtual void setKeepDimensions(bool keepDimensions) TRTNOEXCEPT = 0; + void setKeepDimensions(bool keepDimensions) noexcept + { + mImpl->setKeepDimensions(keepDimensions); + } //! //! \brief Get the boolean that specifies whether or not to keep the reduced dimensions for the layer. //! //! \see setKeepDimensions //! - virtual bool getKeepDimensions() const TRTNOEXCEPT = 0; + bool getKeepDimensions() const noexcept + { + return mImpl->getKeepDimensions(); + } protected: - virtual ~IReduceLayer() {} + apiv::VReduceLayer* mImpl; + virtual ~IReduceLayer() noexcept = default; }; //! @@ -3634,7 +3554,10 @@ public: //! //! \deprecated Superseded by setPrePaddingNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual void setPrePadding(DimsHW padding) TRTNOEXCEPT = 0; + TRT_DEPRECATED void setPrePadding(DimsHW padding) noexcept + { + mImpl->setPrePadding(padding); + } //! //! \brief Get the padding that is applied at the start of the tensor. @@ -3643,7 +3566,10 @@ public: //! //! \deprecated Superseded by getPrePaddingNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual DimsHW getPrePadding() const TRTNOEXCEPT = 0; + TRT_DEPRECATED DimsHW getPrePadding() const noexcept + { + return mImpl->getPrePadding(); + } //! //! \brief Set the padding that is applied at the end of the tensor. @@ -3654,7 +3580,10 @@ public: //! //! \deprecated Superseded by setPostPaddingNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual void setPostPadding(DimsHW padding) TRTNOEXCEPT = 0; + TRT_DEPRECATED void setPostPadding(DimsHW padding) noexcept + { + mImpl->setPostPadding(padding); + } //! //! \brief Get the padding that is applied at the end of the tensor. @@ -3663,51 +3592,66 @@ public: //! //! \deprecated Superseded by getPostPaddingNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual DimsHW getPostPadding() const TRTNOEXCEPT = 0; + TRT_DEPRECATED DimsHW getPostPadding() const noexcept + { + return mImpl->getPostPadding(); + } -protected: - virtual ~IPaddingLayer() {} - -public: //! //! \brief Set the padding that is applied at the start of the tensor. //! //! Negative padding results in trimming the edge by the specified amount. //! - //! \warning Only 2 dimensionsional padding is currently supported. + //! \warning Only 2 dimensional padding is currently supported. //! //! \see getPrePaddingNd //! - virtual void setPrePaddingNd(Dims padding) TRTNOEXCEPT = 0; + void setPrePaddingNd(Dims padding) noexcept + { + mImpl->setPrePaddingNd(padding); + } //! //! \brief Get the padding that is applied at the start of the tensor. //! - //! \warning Only 2 dimensionsional padding is currently supported. + //! \warning Only 2 dimensional padding is currently supported. //! //! \see setPrePaddingNd //! - virtual Dims getPrePaddingNd() const TRTNOEXCEPT = 0; + Dims getPrePaddingNd() const noexcept + { + return mImpl->getPrePaddingNd(); + } //! //! \brief Set the padding that is applied at the end of the tensor. //! //! Negative padding results in trimming the edge by the specified amount //! - //! \warning Only 2 dimensionsional padding is currently supported. + //! \warning Only 2 dimensional padding is currently supported. //! //! \see getPostPaddingNd //! - virtual void setPostPaddingNd(Dims padding) TRTNOEXCEPT = 0; + void setPostPaddingNd(Dims padding) noexcept + { + mImpl->setPostPaddingNd(padding); + } //! //! \brief Get the padding that is applied at the end of the tensor. //! - //! \warning Only 2 dimensionsional padding is currently supported. + //! \warning Only 2 dimensional padding is currently supported. //! //! \see setPostPaddingNd //! - virtual Dims getPostPaddingNd() const TRTNOEXCEPT = 0; + Dims getPostPaddingNd() const noexcept + { + return mImpl->getPostPaddingNd(); + } + +protected: + apiv::VPaddingLayer* mImpl; + virtual ~IPaddingLayer() noexcept = default; }; struct Permutation @@ -3725,7 +3669,7 @@ struct Permutation //! //! \brief Layer type for shuffling data. //! -//! This class shuffles data by applying in sequence: a transpose operation, a reshape operation +//! This layer shuffles data by applying in sequence: a transpose operation, a reshape operation //! and a second transpose operation. The dimension types of the output are those of the reshape dimension. //! //! The layer has an optional second input. If present, it must be a 1D Int32 shape tensor, @@ -3745,7 +3689,10 @@ public: //! //! \see getFirstTranspose //! - virtual void setFirstTranspose(Permutation permutation) TRTNOEXCEPT = 0; + void setFirstTranspose(Permutation permutation) noexcept + { + mImpl->setFirstTranspose(permutation); + } //! //! \brief Get the permutation applied by the first transpose operation. @@ -3754,7 +3701,10 @@ public: //! //! \see setFirstTranspose //! - virtual Permutation getFirstTranspose() const TRTNOEXCEPT = 0; + Permutation getFirstTranspose() const noexcept + { + return mImpl->getFirstTranspose(); + } //! //! \brief Set the reshaped dimensions. @@ -3774,9 +3724,12 @@ public: //! //! The product of the new dimensions must be equal to the product of the old. //! - //! If the second input is set, it is reset to null. + //! If a second input had been used to create this layer, that input is reset to null by this method. //! - virtual void setReshapeDimensions(Dims dimensions) TRTNOEXCEPT = 0; + void setReshapeDimensions(Dims dimensions) noexcept + { + mImpl->setReshapeDimensions(dimensions); + } //! //! \brief Get the reshaped dimensions. @@ -3786,7 +3739,10 @@ public: //! If a second input is present and non-null, or setReshapeDimensions has //! not yet been called, this function returns Dims with nbDims == -1. //! - virtual Dims getReshapeDimensions() const TRTNOEXCEPT = 0; + Dims getReshapeDimensions() const noexcept + { + return mImpl->getReshapeDimensions(); + } //! //! \brief Append or replace an input of this layer with a specific tensor @@ -3807,7 +3763,16 @@ public: //! If this function is called with a value 1, then the function getNbInputs() changes //! from returning 1 to 2. //! - void setInput(int32_t index, ITensor& tensor) _TENSORRT_OVERRIDE TRTNOEXCEPT = 0; + //! The reshape dimensions are treated identically to how they are treated if set statically + //! via setReshapeDimensions. In particular, a -1 is treated as a wildcard even if dynamically + //! supplied at runtime, and a 0 is treated as a placeholder if getZeroIsPlaceholder() = true, + //! which is the default. If the placeholder interpretation of 0 is unwanted because the + //! runtime dimension should be 0 when the reshape dimension is 0, be sure to call + //! setZeroIsPlacholder(false) on the IShuffleLayer. + //! + //! \see setReshapeDimensions. + //! + using ILayer::setInput; //! //! \brief Set the permutation applied by the second transpose operation. @@ -3821,7 +3786,10 @@ public: //! //! \see getSecondTranspose //! - virtual void setSecondTranspose(Permutation permutation) TRTNOEXCEPT = 0; + void setSecondTranspose(Permutation permutation) noexcept + { + mImpl->setSecondTranspose(permutation); + } //! //! \brief Get the permutation applied by the second transpose operation. @@ -3830,12 +3798,11 @@ public: //! //! \see setSecondTranspose //! - virtual Permutation getSecondTranspose() const TRTNOEXCEPT = 0; + Permutation getSecondTranspose() const noexcept + { + return mImpl->getSecondTranspose(); + } -protected: - virtual ~IShuffleLayer() {} - -public: //! //! \brief Set meaning of 0 in reshape dimensions. //! @@ -3847,7 +3814,10 @@ public: //! //! \see getZeroIsPlaceholder(); //! - virtual void setZeroIsPlaceholder(bool zeroIsPlaceholder) = 0; + void setZeroIsPlaceholder(bool zeroIsPlaceholder) noexcept + { + return mImpl->setZeroIsPlaceholder(zeroIsPlaceholder); + } //! //! \brief Get meaning of 0 in reshape dimensions. @@ -3857,7 +3827,14 @@ public: //! //! \see setZeroIsPlaceholder //! - virtual bool getZeroIsPlaceholder() const = 0; + bool getZeroIsPlaceholder() const noexcept + { + return mImpl->getZeroIsPlaceholder(); + } + +protected: + apiv::VShuffleLayer* mImpl; + virtual ~IShuffleLayer() noexcept = default; }; //! @@ -3873,7 +3850,7 @@ enum class SliceMode : int32_t //! Maximum number of elements in SliceMode enum. \see SliceMode template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 2; } @@ -3894,8 +3871,10 @@ constexpr inline int32_t EnumMax() //! copies elements to the output tensor using the specified stride across the input tensor. //! Start, size, and stride tensors must be 1D Int32 shape tensors if not specified via Dims. //! -//! Furthermore, if the slice layer must produce a shape tensor, then start, size, and stride must be -//! build time constants, i.e. as static Dims, or be computable by constant folding. +//! A slice layer can produce a shape tensor if the following conditions are met: +//! +//! * start, size, and stride are build time constants, either as static Dims, or computable by constant folding. +//! * The number of elements in the output tensor does not exceed 2*Dims::MAX_DIMS. //! //! For example using slice on a tensor: //! input = {{0, 2, 4}, {1, 3, 5}} @@ -3914,11 +3893,14 @@ public: //! //! \param start The start offset to read data from the input tensor. //! - //! If the second input is set, it is reset to null. + //! If a second input had been used to create this layer, that input is reset to null by this method. //! //! \see getStart //! - virtual void setStart(Dims start) TRTNOEXCEPT = 0; + void setStart(Dims start) noexcept + { + mImpl->setStart(start); + } //! //! \brief Get the start offset for the slice layer. @@ -3930,18 +3912,24 @@ public: //! //! \see setStart //! - virtual Dims getStart() const TRTNOEXCEPT = 0; + Dims getStart() const noexcept + { + return mImpl->getStart(); + } //! //! \brief Set the dimensions of the output slice. //! //! \param size The dimensions of the output slice. //! - //! If the third input is set, it is reset to null. + //! If a third input had been used to create this layer, that input is reset to null by this method. //! //! \see getSize //! - virtual void setSize(Dims size) TRTNOEXCEPT = 0; + void setSize(Dims size) noexcept + { + return mImpl->setSize(size); + } //! //! \brief Get dimensions of the output slice. @@ -3953,18 +3941,24 @@ public: //! //! \see setSize //! - virtual Dims getSize() const TRTNOEXCEPT = 0; + Dims getSize() const noexcept + { + return mImpl->getSize(); + } //! //! \brief Set the stride for computing the output slice data. //! //! \param stride The dimensions of the stride to compute the values to store in the output slice. //! - //! If the fourth input is set, it is reset to null. + //! If a fourth input had been used to create this layer, that input is reset to null by this method. //! //! \see getStride //! - virtual void setStride(Dims stride) TRTNOEXCEPT = 0; + void setStride(Dims stride) noexcept + { + mImpl->setStride(stride); + } //! //! \brief Get the stride for the output slice. @@ -3976,21 +3970,30 @@ public: //! //! \see setStride //! - virtual Dims getStride() const TRTNOEXCEPT = 0; + Dims getStride() const noexcept + { + return mImpl->getStride(); + } //! //! \brief Set the slice mode. //! //! \see getMode() //! - virtual void setMode(SliceMode mode) TRTNOEXCEPT = 0; + void setMode(SliceMode mode) noexcept + { + mImpl->setMode(mode); + } //! //! \brief Get the slice mode. //! //! \see setMode() //! - virtual SliceMode getMode() const TRTNOEXCEPT = 0; + SliceMode getMode() const noexcept + { + return mImpl->getMode(); + } //! //! \brief Append or replace an input of this layer with a specific tensor @@ -4010,17 +4013,18 @@ public: //! If this function is called with a value greater than 0, then the function getNbInputs() changes //! from returning 1 to index + 1. //! - void setInput(int32_t index, ITensor& tensor) _TENSORRT_OVERRIDE TRTNOEXCEPT = 0; + using ILayer::setInput; protected: - virtual ~ISliceLayer() {} + apiv::VSliceLayer* mImpl; + virtual ~ISliceLayer() noexcept = default; }; //! \class IShapeLayer //! //! \brief Layer type for getting shape of a tensor. //! -//! This class sets the output to a one-dimensional tensor with the dimensions of the input tensor. +//! This layer sets the output to a one-dimensional tensor with the dimensions of the input tensor. //! //! For example, if the input is a four-dimensional tensor (of any type) with //! dimensions [2,3,5,7], the output tensor is a one-dimensional Int32 tensor @@ -4031,7 +4035,8 @@ protected: class IShapeLayer : public ILayer { protected: - virtual ~IShapeLayer() {} + apiv::VShapeLayer* mImpl; + virtual ~IShapeLayer() noexcept = default; }; //! @@ -4047,7 +4052,7 @@ enum class TopKOperation : int32_t //! Maximum number of elements in TopKOperation enum. \see TopKOperation template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 2; } @@ -4067,14 +4072,20 @@ public: //! //! \see getOperation(), TopKOperation //! - virtual void setOperation(TopKOperation op) TRTNOEXCEPT = 0; + void setOperation(TopKOperation op) noexcept + { + mImpl->setOperation(op); + } //! //! \brief Get the operation for the layer. //! //! \see setOperation(), TopKOperation //! - virtual TopKOperation getOperation() const TRTNOEXCEPT = 0; + TopKOperation getOperation() const noexcept + { + return mImpl->getOperation(); + } //! //! \brief Set the k value for the layer. @@ -4083,31 +4094,44 @@ public: //! //! \see getK() //! - virtual void setK(int32_t k) TRTNOEXCEPT = 0; + void setK(int32_t k) noexcept + { + mImpl->setK(k); + } //! //! \brief Get the k value for the layer. //! //! \see setK() //! - virtual int32_t getK() const TRTNOEXCEPT = 0; + int32_t getK() const noexcept + { + return mImpl->getK(); + } //! //! \brief Set which axes to reduce for the layer. //! //! \see getReduceAxes() //! - virtual void setReduceAxes(uint32_t reduceAxes) TRTNOEXCEPT = 0; + void setReduceAxes(uint32_t reduceAxes) noexcept + { + mImpl->setReduceAxes(reduceAxes); + } //! //! \brief Get the axes to reduce for the layer. //! //! \see setReduceAxes() //! - virtual uint32_t getReduceAxes() const TRTNOEXCEPT = 0; + uint32_t getReduceAxes() const noexcept + { + return mImpl->getReduceAxes(); + } protected: - virtual ~ITopKLayer() {} + apiv::VTopKLayer* mImpl; + virtual ~ITopKLayer() noexcept = default; }; //! @@ -4140,7 +4164,7 @@ enum class MatrixOperation : int32_t //! Maximum number of elements in MatrixOperation enum. \see DataType template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 3; } @@ -4177,38 +4201,26 @@ public: //! \brief Set the operation for an input tensor. //! \param index Input tensor number (0 or 1). //! \param op New operation. - //! \see getTranspose() + //! \see getOperation() //! - virtual void setOperation(int32_t index, MatrixOperation op) TRTNOEXCEPT = 0; + void setOperation(int32_t index, MatrixOperation op) noexcept + { + mImpl->setOperation(index, op); + } //! //! \brief Get the operation for an input tensor. //! \param index Input tensor number (0 or 1). - //! \see setTranspose() + //! \see setOperation() //! - virtual MatrixOperation getOperation(int32_t index) const TRTNOEXCEPT = 0; - - //! - //! \brief Set the transpose flag for an input tensor. - //! \param index Input tensor number (0 or 1). - //! \param val New transpose flag. - //! \see getTranspose() - //! - //! \deprecated setTranspose is superseded by setOperation and will be removed in TensorRT 8.0. - //! - TRT_DEPRECATED virtual void setTranspose(int32_t index, bool val) TRTNOEXCEPT = 0; - - //! - //! \brief Get the transpose flag for an input tensor. - //! \param index Input tensor number (0 or 1). - //! \see setTranspose() - //! - //! \deprecated getTranspose is superseded by getOperation and will be removed in TensorRT 8.0. - //! - TRT_DEPRECATED virtual bool getTranspose(int32_t index) const TRTNOEXCEPT = 0; + MatrixOperation getOperation(int32_t index) const noexcept + { + return mImpl->getOperation(index); + } protected: - virtual ~IMatrixMultiplyLayer() {} + apiv::VMatrixMultiplyLayer* mImpl; + virtual ~IMatrixMultiplyLayer() noexcept = default; }; //! @@ -4228,23 +4240,26 @@ protected: class IRaggedSoftMaxLayer : public ILayer { protected: - virtual ~IRaggedSoftMaxLayer() {} + apiv::VRaggedSoftMaxLayer* mImpl; + virtual ~IRaggedSoftMaxLayer() noexcept = default; }; //! \class IIdentityLayer //! //! \brief A layer that represents the identity function. //! -//! If tensor precision is being explicitly specified, it can be used to transform from one precision to another. -//! Other than transforming between the same precision (kFLOAT -> kFLOAT for example), the only valid -//! tranformations supported are: (kFLOAT -> kHALF), (kFLOAT -> kINT8), (kHALF -> kFLOAT) and (kINT8 -> kFLOAT). +//! If tensor precision is being explicitly specified, it can be used to convert from one precision to another. +//! Other than conversion between the same precision (kFLOAT -> kFLOAT for example), the only valid +//! tranformations supported are: (kHALF -> kINT32), (kHALF -> kFLOAT), (kFLOAT -> kINT32), (kINT32 -> kHALF), +//! (kINT32 -> kFLOAT), (kBOOL -> kBOOL), (kBOOL -> kHALF), (kBOOL -> kFLOAT). //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. //! class IIdentityLayer : public ILayer { protected: - virtual ~IIdentityLayer() {} + apiv::VIdentityLayer* mImpl; + virtual ~IIdentityLayer() noexcept = default; }; //! \class IConstantLayer @@ -4266,14 +4281,20 @@ public: //! //! \see getWeights() //! - virtual void setWeights(Weights weights) TRTNOEXCEPT = 0; + void setWeights(Weights weights) noexcept + { + mImpl->setWeights(weights); + } //! //! \brief Get the weights for the layer. //! //! \see setWeights //! - virtual Weights getWeights() const TRTNOEXCEPT = 0; + Weights getWeights() const noexcept + { + return mImpl->getWeights(); + } //! //! \brief Set the dimensions for the layer. @@ -4282,7 +4303,10 @@ public: //! //! \see setDimensions //! - virtual void setDimensions(Dims dimensions) TRTNOEXCEPT = 0; + void setDimensions(Dims dimensions) noexcept + { + mImpl->setDimensions(dimensions); + } //! //! \brief Get the dimensions for the layer. @@ -4291,10 +4315,14 @@ public: //! //! \see getDimensions //! - virtual Dims getDimensions() const TRTNOEXCEPT = 0; + Dims getDimensions() const noexcept + { + return mImpl->getDimensions(); + } protected: - virtual ~IConstantLayer() {} + apiv::VConstantLayer* mImpl; + virtual ~IConstantLayer() noexcept = default; }; //! @@ -4307,7 +4335,8 @@ protected: class IParametricReLULayer : public ILayer { protected: - virtual ~IParametricReLULayer() noexcept {} + apiv::VParametricReLULayer* mImpl; + virtual ~IParametricReLULayer() noexcept = default; }; //! \enum ResizeMode @@ -4321,24 +4350,137 @@ enum class ResizeMode : int32_t kLINEAR = 1 //!< Can handle linear (1D), bilinear (2D), and trilinear (3D) resizing. }; +namespace impl +{ //! Maximum number of elements in ResizeMode enum. \see ResizeMode template <> -constexpr inline int32_t EnumMax() +struct EnumMaxImpl { - return 2; -} + static constexpr int32_t kVALUE = 2; +}; +} // namespace impl + +//! +//! \enum ResizeCoordinateTransformation +//! +//! \brief The resize coordinate transformation function. +//! +//! \see IResizeLayer::setCoordinateTransformation() +//! +enum class ResizeCoordinateTransformation : int32_t +{ + //! We can think each value in tensor has a volume, and the coordinate is a point inside this volume. + //! The coordinate point is drawn as star(*) in below diagram, and multiple values range has a length. + //! Let's use x_origin as the coordinate of axis x in the input tensor, x_resized as the coordinate of axis x in the + //! output tensor, length_origin as length of the input tensor in axis x, and length_resize as length of the output + //! tensor in axis x. + //! + //! |<--------------length---------->| + //! | 0 | 1 | 2 | 3 | + //! * * * * + //! + //! x_origin = x_resized * (length_origin - 1) / (length_resize - 1) + //! + kALIGN_CORNERS = 0, + + //! |<--------------length--------------------->| + //! | 0 | 1 | 2 | 3 | + //! * * * * + //! + //! x_origin = x_resized * (length_origin / length_resize) + //! + kASYMMETRIC = 1, + + //! |<--------------length--------------------->| + //! | 0 | 1 | 2 | 3 | + //! * * * * + //! + //! x_origin = (x_resized + 0.5) * (length_origin / length_resize) - 0.5 + //! + kHALF_PIXEL = 2, +}; + +namespace impl +{ +//! Maximum number of elements in ResizeCoordinateTransformation enum. \see ResizeCoordinateTransformation +template <> +struct EnumMaxImpl +{ + static constexpr int32_t kVALUE = 3; +}; +} // namespace impl + +//! +//! \enum ResizeSelector +//! +//! \brief The coordinate selector when resize to single pixel output. +//! +//! \see IResizeLayer::setSelectorForSinglePixel() +//! +enum class ResizeSelector : int32_t +{ + //! Use formula to map the original index. + kFORMULA = 0, + + //! Select the upper left pixel. + kUPPER = 1, +}; + +namespace impl +{ +//! Maximum number of elements in ResizeSelector enum. \see ResizeSelector +template <> +struct EnumMaxImpl +{ + static constexpr int32_t kVALUE = 2; +}; +} // namespace impl + +//! +//! \enum ResizeRoundMode +//! +//! \brief The rounding mode for nearest neighbor resize. +//! +//! +//! \see IResizeLayer::setNearestRounding() +//! +enum class ResizeRoundMode : int32_t +{ + //! Round half up. + kHALF_UP = 0, + + //! Round half down. + kHALF_DOWN = 1, + + //! Round to floor. + kFLOOR = 2, + + //! Round to ceil. + kCEIL = 3, +}; + +namespace impl +{ +//! Maximum number of elements in ResizeRoundMode enum. \see ResizeRoundMode +template <> +struct EnumMaxImpl +{ + static constexpr int32_t kVALUE = 4; +}; +} // namespace impl //! \class IResizeLayer //! //! \brief A resize layer in a network definition. //! -//! Resize layer can be used for resizing a ND tensor. +//! Resize layer can be used for resizing a N-D tensor. //! //! Resize layer currently supports the following configurations: -//! - ResizeMode::kNEAREST - resizes innermost `m` dimensions of ND, where 0 < m <= min(8, N) and N > 0 -//! - ResizeMode::kLINEAR - resizes innermost `m` dimensions of ND, where 0 < m <= min(3, N) and N > 0 +//! - ResizeMode::kNEAREST - resizes innermost `m` dimensions of N-D, where 0 < m <= min(8, N) and N > 0 +//! - ResizeMode::kLINEAR - resizes innermost `m` dimensions of N-D, where 0 < m <= min(3, N) and N > 0 //! //! Default resize mode is ResizeMode::kNEAREST. +//! //! Resize layer provides two ways to resize tensor dimensions. //! - Set output dimensions directly. It can be done for static as well as dynamic resize layer. //! Static resize layer requires output dimensions to be known at build-time. @@ -4354,7 +4496,8 @@ public: //! //! \brief Set the output dimensions. //! - //! \param dimensions The output dimensions. Number of output dimensions must be the same as the number of input dimensions. + //! \param dimensions The output dimensions. Number of output dimensions must be the same as the number of input + //! dimensions. //! //! If there is a second input, i.e. resize layer is dynamic, //! calling setOutputDimensions() is an error and does not update the @@ -4366,14 +4509,20 @@ public: //! \see setScales //! \see getOutputDimensions //! - virtual void setOutputDimensions(Dims dimensions) TRTNOEXCEPT = 0; + void setOutputDimensions(Dims dimensions) noexcept + { + return mImpl->setOutputDimensions(dimensions); + } //! //! \brief Get the output dimensions. //! //! \return The output dimensions. //! - virtual Dims getOutputDimensions() const TRTNOEXCEPT = 0; + Dims getOutputDimensions() const noexcept + { + return mImpl->getOutputDimensions(); + } //! //! \brief Set the resize scales. @@ -4393,7 +4542,10 @@ public: //! \see setOutputDimensions //! \see getScales //! - virtual void setScales(const float* scales, int32_t nbScales) TRTNOEXCEPT = 0; + void setScales(const float* scales, int32_t nbScales) noexcept + { + mImpl->setScales(scales, nbScales); + } //! //! \brief Copies resize scales to scales[0, ..., nbScales-1], where nbScales is the number of scales that were set. @@ -4409,7 +4561,10 @@ public: //! \return The number of resize scales i.e. nbScales if scales were set. //! Return -1 in case no scales were set or resize layer is used in dynamic mode. //! - virtual int32_t getScales(int32_t size, float* scales) const TRTNOEXCEPT = 0; + int32_t getScales(int32_t size, float* scales) const noexcept + { + return mImpl->getScales(size, scales); + } //! //! \brief Set resize mode for an input tensor. @@ -4418,14 +4573,20 @@ public: //! //! \see ResizeMode //! - virtual void setResizeMode(ResizeMode resizeMode) TRTNOEXCEPT = 0; + void setResizeMode(ResizeMode resizeMode) noexcept + { + mImpl->setResizeMode(resizeMode); + } //! //! \brief Get resize mode for an input tensor. //! //! \return The resize mode. //! - virtual ResizeMode getResizeMode() const TRTNOEXCEPT = 0; + ResizeMode getResizeMode() const noexcept + { + return mImpl->getResizeMode(); + } //! //! \brief Set whether to align corners while resizing. @@ -4436,20 +4597,32 @@ public: //! //! Default: false. //! - virtual void setAlignCorners(bool alignCorners) TRTNOEXCEPT = 0; + //! \deprecated Superseded by IResizeLayer::setCoordinateTransformation() and + //! will be removed in TensorRT 10.0. + //! + TRT_DEPRECATED void setAlignCorners(bool alignCorners) noexcept + { + mImpl->setAlignCorners(alignCorners); + } //! //! \brief True if align corners has been set. //! //! \return True if align corners has been set, false otherwise. //! - virtual bool getAlignCorners() const TRTNOEXCEPT = 0; + //! \deprecated Superseded by IResizeLayer::getCoordinateTransformation() and + //! will be removed in TensorRT 10.0. + //! + TRT_DEPRECATED bool getAlignCorners() const noexcept + { + return mImpl->getAlignCorners(); + } //! //! \brief Append or replace an input of this layer with a specific tensor //! //! \param index the index of the input to modify. - //! \param tensor the new input tensor + //! \param tensor the new input tensor. //! //! Sets the input tensor for the given index. The index must be 0 for a static resize layer. //! A static resize layer is converted to a dynamic resize layer by calling setInput with an index 1. @@ -4464,10 +4637,84 @@ public: //! If this function is called with a value 1, then the function getNbInputs() changes //! from returning 1 to 2. //! - void setInput(int32_t index, ITensor& tensor) _TENSORRT_OVERRIDE TRTNOEXCEPT = 0; + using ILayer::setInput; + + //! + //! \brief Set coordinate transformation function. + //! + //! We have different functions mapping the coordinate in output tensor to the coordinate in input tensor. + //! + //! Default is ResizeCoordinateTransformation::kASYMMETRIC. + //! + //! \see ResizeCoordinateTransformation + //! + void setCoordinateTransformation(ResizeCoordinateTransformation coordTransform) noexcept + { + mImpl->setCoordinateTransformation(coordTransform); + } + + //! + //! \brief Get coordinate transformation function. + //! + //! \return The coordinate transformation function. + //! + ResizeCoordinateTransformation getCoordinateTransformation() const noexcept + { + return mImpl->getCoordinateTransformation(); + } + + //! + //! \brief Set coordinate selector function when resized to single pixel. + //! + //! When resize to single pixel image, use this function to decide how to map the coordinate in the original + //! image. + //! + //! Default is ResizeSelector::kFORMULA. + //! + //! \see ResizeSelector + //! + void setSelectorForSinglePixel(ResizeSelector selector) noexcept + { + mImpl->setSelectorForSinglePixel(selector); + } + + //! + //! \brief Get the coordinate selector function when resized to single pixel. + //! + //! \return The selector function. + //! + ResizeSelector getSelectorForSinglePixel() const noexcept + { + return mImpl->getSelectorForSinglePixel(); + } + + //! + //! \brief Set rounding mode for nearest neighbor resize. + //! + //! This value is used for nearest neighbor interpolation rounding. It is applied after coordinate transformation. + //! + //! Default is kFLOOR. + //! + //! \see ResizeRoundMode + //! + void setNearestRounding(ResizeRoundMode value) noexcept + { + mImpl->setNearestRounding(value); + } + + //! + //! \brief Get rounding mode for nearest neighbor resize. + //! + //! \return The rounding mode. + //! + ResizeRoundMode getNearestRounding() const noexcept + { + return mImpl->getNearestRounding(); + } protected: - virtual ~IResizeLayer() {} + virtual ~IResizeLayer() noexcept = default; + apiv::VResizeLayer* mImpl; }; //! Enum that describes kinds of loop outputs. @@ -4485,7 +4732,7 @@ enum class LoopOutput : int32_t //! Maximum number of elements in LoopOutput enum. \see DataType template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 3; } @@ -4500,7 +4747,7 @@ enum class TripLimit : int32_t //! Maximum number of elements in TripLimit enum. \see DataType template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 2; } @@ -4511,7 +4758,14 @@ class ILoopBoundaryLayer : public ILayer { public: //! Return pointer to ILoop associated with this boundary layer. - virtual ILoop* getLoop() const noexcept = 0; + ILoop* getLoop() const noexcept + { + return mBoundary->getLoop(); + } + +protected: + virtual ~ILoopBoundaryLayer() noexcept = default; + apiv::VLoopBoundaryLayer* mBoundary; }; class IRecurrenceLayer : public ILoopBoundaryLayer @@ -4535,7 +4789,11 @@ public: //! If this function is called with a value 1, then the function getNbInputs() changes //! from returning 1 to 2. //! - void setInput(int32_t index, ITensor& tensor) _TENSORRT_OVERRIDE TRTNOEXCEPT = 0; + using ILayer::setInput; + +protected: + virtual ~IRecurrenceLayer() noexcept = default; + apiv::VRecurrenceLayer* mImpl; }; //! @@ -4558,7 +4816,10 @@ public: class ILoopOutputLayer : public ILoopBoundaryLayer { public: - virtual LoopOutput getLoopOutput() const noexcept = 0; + LoopOutput getLoopOutput() const noexcept + { + return mImpl->getLoopOutput(); + } //! //! \brief Set where to insert the contenation axis. Ignored if getLoopOutput() is kLAST_VALUE. @@ -4572,10 +4833,16 @@ public: //! setAxis(3) causes the output to have dimensions [b,c,d,a]. //! Default is axis is 0. //! - virtual void setAxis(int32_t axis) noexcept = 0; + void setAxis(int32_t axis) noexcept + { + mImpl->setAxis(axis); + } //! Get axis being concatenated over. - virtual int32_t getAxis() const noexcept = 0; + int32_t getAxis() const noexcept + { + return mImpl->getAxis(); + } //! //! \brief Append or replace an input of this layer with a specific tensor @@ -4597,49 +4864,80 @@ public: //! If this function is called with a value 1, then the function getNbInputs() changes //! from returning 1 to 2. //! - void setInput(int32_t index, ITensor& tensor) _TENSORRT_OVERRIDE TRTNOEXCEPT = 0; + using ILayer::setInput; + +protected: + virtual ~ILoopOutputLayer() noexcept = default; + apiv::VLoopOutputLayer* mImpl; }; class ITripLimitLayer : public ILoopBoundaryLayer { public: - virtual TripLimit getTripLimit() const noexcept = 0; + TripLimit getTripLimit() const noexcept + { + return mImpl->getTripLimit(); + } + +protected: + virtual ~ITripLimitLayer() noexcept = default; + apiv::VTripLimitLayer* mImpl; }; class IIteratorLayer : public ILoopBoundaryLayer { public: //! Set axis to iterate over. - virtual void setAxis(int32_t axis) noexcept = 0; + void setAxis(int32_t axis) noexcept + { + mImpl->setAxis(axis); + } //! Get axis being iterated over. - virtual int32_t getAxis() const noexcept = 0; + int32_t getAxis() const noexcept + { + return mImpl->getAxis(); + } //! For reverse=false, the layer is equivalent to addGather(tensor, I, 0) where I is a //! scalar tensor containing the loop iteration number. //! For reverse=true, the layer is equivalent to addGather(tensor, M-1-I, 0) where M is the trip count //! computed from TripLimits of kind kCOUNT. //! The default is reverse=false. - virtual void setReverse(bool reverse) noexcept = 0; + void setReverse(bool reverse) noexcept + { + mImpl->setReverse(reverse); + } //! True if and only if reversing input. - virtual bool getReverse() const noexcept = 0; + bool getReverse() const noexcept + { + return mImpl->getReverse(); + } + +protected: + virtual ~IIteratorLayer() noexcept = default; + apiv::VIteratorLayer* mImpl; }; //! //! Helper for creating a recurrent subgraph. //! -class ILoop +//! An ILoop cannot be added to an INetworkDefinition where hasImplicitBatchDimensions() returns true. +//! +class ILoop : public INoCopy { public: //! //! \brief Create a recurrence layer for this loop with initialValue as its first input. //! - //! IRecurrenceLayer requires exactly two inputs. The 2nd input must be added, via method IRecurrenceLayer::setInput(1,...) - //! before an Engine can be built. - // + //! IRecurrenceLayer requires exactly two inputs. The 2nd input must be added, via method + //! IRecurrenceLayer::setInput(1,...) before an Engine can be built. //! - virtual IRecurrenceLayer* addRecurrence(ITensor& initialValue) noexcept = 0; + IRecurrenceLayer* addRecurrence(ITensor& initialValue) noexcept + { + return mImpl->addRecurrence(initialValue); + } //! //! \brief Add a trip-count limiter, based on the given tensor. @@ -4649,7 +4947,7 @@ public: //! count is reached or condition is falsified. //! It is an error to not add at least one trip limiter. //! - //! For kTRIP_LIMIT, the input tensor must be available before the loop starts. + //! For kCOUNT, the input tensor must be available before the loop starts. //! //! For kWHILE, the input tensor must be the output of a subgraph that contains //! only layers that are not ITripLimitLayer, IIteratorLayer or ILoopOutputLayer. @@ -4657,7 +4955,10 @@ public: //! ITripLimitLayer. A trivial example of this rule is that the input to the kWHILE //! is the output of an IRecurrenceLayer for the same loop. //! - virtual ITripLimitLayer* addTripLimit(ITensor& tensor, TripLimit limit) noexcept = 0; + ITripLimitLayer* addTripLimit(ITensor& tensor, TripLimit limit) noexcept + { + return mImpl->addTripLimit(tensor, limit); + } //! //! \brief Return layer that subscripts tensor by loop iteration. @@ -4667,7 +4968,10 @@ public: //! For reverse=true, this is equivalent to addGather(tensor, M-1-I, 0) where M is the trip count //! computed from TripLimits of kind kCOUNT. //! - virtual IIteratorLayer* addIterator(ITensor& tensor, int32_t axis = 0, bool reverse = false) noexcept = 0; + IIteratorLayer* addIterator(ITensor& tensor, int32_t axis = 0, bool reverse = false) noexcept + { + return mImpl->addIterator(tensor, axis, reverse); + } //! \brief Make an output for this loop, based on the given tensor. //! @@ -4676,7 +4980,10 @@ public: //! If outputKind is kCONCATENATE or kREVERSE, a second input specifying the //! concatenation dimension must be added via method ILoopOutputLayer::setInput. //! - virtual ILoopOutputLayer* addLoopOutput(ITensor& tensor, LoopOutput outputKind, int32_t axis = 0) noexcept = 0; + ILoopOutputLayer* addLoopOutput(ITensor& tensor, LoopOutput outputKind, int32_t axis = 0) noexcept + { + return mImpl->addLoopOutput(tensor, outputKind, axis); + } //! //! \brief Set the name of the loop. @@ -4686,17 +4993,24 @@ public: //! //! \see getName() //! - virtual void setName(const char* name) noexcept = 0; + void setName(const char* name) noexcept + { + mImpl->setName(name); + } //! //! \brief Return the name of the loop. //! //! \see setName() //! - virtual const char* getName() const noexcept = 0; + const char* getName() const noexcept + { + return mImpl->getName(); + } protected: - virtual ~ILoop() {} + virtual ~ILoop() noexcept = default; + apiv::VLoop* mImpl; }; //! @@ -4705,7 +5019,8 @@ protected: class ISelectLayer : public ILayer { protected: - virtual ~ISelectLayer() {} + virtual ~ISelectLayer() noexcept = default; + apiv::VSelectLayer* mImpl; }; //! @@ -4723,7 +5038,7 @@ enum class FillOperation : int32_t //! Maximum number of elements in FillOperation enum. \see FillOperation template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 2; } @@ -4743,6 +5058,13 @@ constexpr inline int32_t EnumMax() //! Alpha and Beta are treated differently based on the Fill Operation specified. See details in //! IFillLayer::setAlpha(), IFillLayer::setBeta(), and IFillLayer::setInput(). //! +//! A fill layer can produce a shape tensor if the following restrictions are met: +//! +//! * The FillOperation is kLINSPACE. +//! * The output is a 1D Int32 tensor with length not exceeding 2*Dims::MAX_DIMS. +//! * There is at most one input, and if so, that input is input 0. +//! * If input 0 exists, the length of the output tensor must be computable by constant folding. +//! //! \see FillOperation //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. @@ -4754,11 +5076,14 @@ public: //! //! \param dimensions The output tensor's dimensions. //! - //! If the first input is set, it is reset to null. + //! If the first input had been used to create this layer, that input is reset to null by this method. //! //! \see getDimensions // - virtual void setDimensions(Dims dimensions) noexcept = 0; + void setDimensions(Dims dimensions) noexcept + { + mImpl->setDimensions(dimensions); + } //! //! \brief Get the output tensor's dimensions. @@ -4770,21 +5095,30 @@ public: //! //! \see setDimensions //! - virtual Dims getDimensions() const noexcept = 0; + Dims getDimensions() const noexcept + { + return mImpl->getDimensions(); + } //! //! \brief Set the fill operation for the layer. //! //! \see getOperation(), FillOperation //! - virtual void setOperation(FillOperation op) noexcept = 0; + void setOperation(FillOperation op) noexcept + { + mImpl->setOperation(op); + } //! //! \brief Get the fill operation for the layer. //! //! \see setOperation(), FillOperation //! - virtual FillOperation getOperation() const noexcept = 0; + FillOperation getOperation() const noexcept + { + return mImpl->getOperation(); + } //! //! \brief Set the alpha parameter. @@ -4795,11 +5129,14 @@ public: //! kLINSPACE | the start value; //! kRANDOMUNIFORM | the minimum value; //! - //! If the second input is set, it is reset to null. + //! If a second input had been used to create this layer, that input is reset to null by this method. //! //! \see getAlpha // - virtual void setAlpha(double alpha) noexcept = 0; + void setAlpha(double alpha) noexcept + { + mImpl->setAlpha(alpha); + } //! //! \brief Get the value of alpha parameter. @@ -4811,7 +5148,10 @@ public: //! //! \see setAlpha //! - virtual double getAlpha() const noexcept = 0; + double getAlpha() const noexcept + { + return mImpl->getAlpha(); + } //! //! \brief Set the beta parameter. @@ -4822,11 +5162,14 @@ public: //! kLINSPACE | the delta value; //! kRANDOMUNIFORM | the maximal value; //! - //! If the third input is set, it is reset to null. + //! If a third input had been used to create this layer, that input is reset to null by this method. //! //! \see getBeta //! - virtual void setBeta(double beta) noexcept = 0; + void setBeta(double beta) noexcept + { + mImpl->setBeta(beta); + } //! //! \brief Get the value of beta parameter. @@ -4838,7 +5181,10 @@ public: //! //! \see setBeta //! - virtual double getBeta() const noexcept = 0; + double getBeta() const noexcept + { + return mImpl->getBeta(); + } //! //! \brief replace an input of this layer with a specific tensor. @@ -4866,10 +5212,187 @@ public: //! then afterwards getNbInputs() returns index + 1, and any missing intervening //! inputs are set to null. //! - void setInput(int32_t index, ITensor& tensor) _TENSORRT_OVERRIDE TRTNOEXCEPT = 0; + using ILayer::setInput; protected: - virtual ~IFillLayer() {} + virtual ~IFillLayer() noexcept = default; + apiv::VFillLayer* mImpl; +}; + +//! +//! \class IQuantizeLayer +//! +//! \brief A Quantize layer in a network definition. +//! +//! This layer accepts a floating-point data input tensor, and uses the scale and zeroPt inputs to +//! quantize the data to an 8-bit signed integer according to: +//! \p output = clamp(round(\p input / \p scale) + \p zeroPt) +//! +//! Rounding type is rounding-to-nearest ties-to-even (https://en.wikipedia.org/wiki/Rounding#Round_half_to_even). +//! Clamping is in the range [-128, 127]. +//! +//! The first input (index 0) is the tensor to be quantized. +//! The second (index 1) and third (index 2) are the scale and zero point respectively. +//! Each of \p scale and \p zeroPt must be either a scalar, or a 1D tensor. +//! +//! The \p zeroPt tensor is optional, and if not set, will be assumed to be zero. Its data type must be +//! DataType::kINT8. \p zeroPt must only contain zero-valued coefficients, because only symmetric quantization is +//! supported. +//! The \p scale value must be either a scalar for per-tensor quantization, or a 1D tensor for per-channel +//! quantization. All \p scale coefficients must have positive values. The size of the 1-D \p scale tensor must match +//! the size of the quantization axis. The size of the \p scale must match the size of the \p zeroPt. +//! +//! The subgraph which terminates with the \p scale tensor must be a build-time constant. The same restrictions apply +//! to the \p zeroPt. +//! The output type, if constrained, must be constrained to DataType::kINT8. The input type, if constrained, must be +//! constrained to DataType::kFLOAT (FP16 input is not supported). +//! The output size is the same as the input size. The quantization axis is in reference to the input tensor's +//! dimensions. +//! +//! IQuantizeLayer only supports DataType::kFLOAT precision and will default to this precision during instantiation. +//! IQuantizeLayer only supports DataType::kINT8 output. +//! +//! As an example of the operation of this layer, imagine a 4D NCHW activation input which can be quantized using a +//! single scale coefficient (referred to as per-tensor quantization): +//! For each n in N: +//! For each c in C: +//! For each h in H: +//! For each w in W: +//! output[n,c,h,w] = clamp(round(\p input[n,c,h,w] / \p scale) + \p zeroPt) +//! +//! Per-channel quantization is supported only for weight inputs. Thus, Activations cannot be quantized per-channel. +//! As an example of per-channel operation, imagine a 4D KCRS weights input and K (dimension 0) as the quantization +//! axis. The scale is an array of coefficients, and must have the same size as the quantization axis. +//! For each k in K: +//! For each c in C: +//! For each r in R: +//! For each s in S: +//! output[k,c,r,s] = clamp(round(\p input[k,c,r,s] / \p scale[k]) + \p zeroPt[k]) +//! +//! \note Only symmetric quantization is supported. +//! \note Currently the only allowed build-time constant \p scale and \zeroPt subgraphs are: +//! 1. Constant -> Quantize +//! 2. Constant -> Cast -> Quantize +//! +//! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. +//! +class IQuantizeLayer : public ILayer +{ +public: + //! + //! \brief Get the quantization axis. + //! + //! \return axis parameter set by setAxis(). + //! The return value is the index of the quantization axis in the input tensor's dimensions. + //! A value of -1 indicates per-tensor quantization. + //! The default value is -1. + //! + int32_t getAxis() const noexcept + { + return mImpl->getAxis(); + } + //! + //! \brief Set the quantization axis. + //! + //! Set the index of the quantization axis (with reference to the input tensor's dimensions). + //! The axis must be a valid axis if the scale tensor has more than one coefficient. + //! The axis value will be ignored if the scale tensor has exactly one coefficient (per-tensor quantization). + //! + void setAxis(int32_t axis) noexcept + { + mImpl->setAxis(axis); + } + +protected: + virtual ~IQuantizeLayer() noexcept = default; + apiv::VQuantizeLayer* mImpl; +}; + +//! +//! \class IDequantizeLayer +//! +//! \brief A Dequantize layer in a network definition. +//! +//! This layer accepts a signed 8-bit integer input tensor, and uses the configured scale and zeroPt inputs to +//! dequantize the input according to: +//! \p output = (\p input - \p zeroPt) * \p scale +//! +//! The first input (index 0) is the tensor to be quantized. +//! The second (index 1) and third (index 2) are the scale and zero point respectively. +//! Each of \p scale and \p zeroPt must be either a scalar, or a 1D tensor. +//! +//! The \p zeroPt tensor is optional, and if not set, will be assumed to be zero. Its data type must be +//! DataType::kINT8. \p zeroPt must only contain zero-valued coefficients, because only symmetric quantization is +//! supported. +//! The \p scale value must be either a scalar for per-tensor quantization, or a 1D tensor for per-channel +//! quantization. All \p scale coefficients must have positive values. The size of the 1-D \p scale tensor must match +//! the size of the quantization axis. The size of the \p scale must match the size of the \p zeroPt. +//! +//! The subgraph which terminates with the \p scale tensor must be a build-time constant. The same restrictions apply +//! to the \p zeroPt. +//! The output type, if constrained, must be constrained to DataType::kINT8. The input type, if constrained, must be +//! constrained to DataType::kFLOAT (FP16 input is not supported). +//! The output size is the same as the input size. The quantization axis is in reference to the input tensor's +//! dimensions. +//! +//! IDequantizeLayer only supports DataType::kINT8 precision and will default to this precision during instantiation. +//! IDequantizeLayer only supports DataType::kFLOAT output. +//! +//! As an example of the operation of this layer, imagine a 4D NCHW activation input which can be quantized using a +//! single scale coefficient (referred to as per-tensor quantization): +//! For each n in N: +//! For each c in C: +//! For each h in H: +//! For each w in W: +//! output[n,c,h,w] = (\p input[n,c,h,w] - \p zeroPt) * \p scale +//! +//! Per-channel dequantization is supported only for input that is rooted at an IConstantLayer (i.e. weights). +//! Activations cannot be quantized per-channel. As an example of per-channel operation, imagine a 4D KCRS weights input +//! and K (dimension 0) as the quantization axis. The scale is an array of coefficients, which is the same size as the +//! quantization axis. +//! For each k in K: +//! For each c in C: +//! For each r in R: +//! For each s in S: +//! output[k,c,r,s] = (\p input[k,c,r,s] - \p zeroPt[k]) * \p scale[k] +//! +//! \note Only symmetric quantization is supported. +//! \note Currently the only allowed build-time constant \p scale and \zeroPt subgraphs are: +//! 1. Constant -> Quantize +//! 2. Constant -> Cast -> Quantize +//! +//! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. +//! +class IDequantizeLayer : public ILayer +{ +public: + //! + //! \brief Get the quantization axis. + //! + //! \return axis parameter set by setAxis(). + //! The return value is the index of the quantization axis in the input tensor's dimensions. + //! A value of -1 indicates per-tensor quantization. + //! The default value is -1. + //! + int32_t getAxis() const noexcept + { + return mImpl->getAxis(); + } + //! + //! \brief Set the quantization axis. + //! + //! Set the index of the quantization axis (with reference to the input tensor's dimensions). + //! The axis must be a valid axis if the scale tensor has more than one coefficient. + //! The axis value will be ignored if the scale tensor has exactly one coefficient (per-tensor quantization). + //! + void setAxis(int32_t axis) noexcept + { + mImpl->setAxis(axis); + } + +protected: + virtual ~IDequantizeLayer() noexcept = default; + apiv::VDequantizeLayer* mImpl; }; //! @@ -4880,7 +5403,7 @@ protected: //! A network definition defines the structure of the network, and combined with a IBuilderConfig, is built //! into an engine using an IBuilder. An INetworkDefinition can either have an implicit batch dimensions, specified //! at runtime, or all dimensions explicit, full dims mode, in the network definition. When a network has been -//! created using createNetwork(), only implicit batch size mode is supported. The function hasImplicitBatchSize() +//! created using createNetwork(), only implicit batch size mode is supported. The function hasImplicitBatchDimension() //! is used to query the mode of the network. //! //! A network with implicit batch dimensions returns the dimensions of a layer without the implicit dimension, @@ -4891,15 +5414,17 @@ protected: //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. //! -class INetworkDefinition +class INetworkDefinition : public INoCopy { public: + virtual ~INetworkDefinition() noexcept = default; + //! //! \brief Add an input tensor to the network. //! //! The name of the input tensor is used to find the index into the buffer array for an engine built from //! the network. The volume of the dimensions must be less than 2^30 elements. - + //! //! For networks with an implicit batch dimension, this volume includes the batch dimension with its length set //! to the maximum batch size. For networks with all explicit dimensions and with wildcard dimensions, the volume //! is based on the maxima specified by an IOptimizationProfile.Dimensions are normally non-negative integers. The @@ -4907,7 +5432,7 @@ public: //! be specified at runtime. Input tensors with such a wildcard must have a corresponding entry in the //! IOptimizationProfiles indicating the permitted extrema, and the input dimensions must be set by //! IExecutionContext::setBindingDimensions. Different IExecutionContext instances can have different dimensions. - //! Wildcard dimensions are only supported for EngineCapability::kDEFAULT. They are not + //! Wildcard dimensions are only supported for EngineCapability::kSTANDARD. They are not //! supported in safety contexts. DLA does not support Wildcard dimensions. //! //! Tensor dimensions are specified independent of format. For example, if a @@ -4929,7 +5454,10 @@ public: //! //! \return The new tensor or nullptr if there is an error. //! - virtual ITensor* addInput(const char* name, DataType type, Dims dimensions) TRTNOEXCEPT = 0; + ITensor* addInput(const char* name, DataType type, Dims dimensions) noexcept + { + return mImpl->addInput(name, type, dimensions); + } //! //! \brief Mark a tensor as a network output. @@ -4938,7 +5466,10 @@ public: //! //! \warning It is an error to mark a network input as an output. //! - virtual void markOutput(ITensor& tensor) TRTNOEXCEPT = 0; + void markOutput(ITensor& tensor) noexcept + { + mImpl->markOutput(tensor); + } //! //! \brief Add a convolution layer to the network. @@ -4958,8 +5489,11 @@ public: //! //! \deprecated Superseded by addConvolutionNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual IConvolutionLayer* addConvolution(ITensor& input, int32_t nbOutputMaps, DimsHW kernelSize, - Weights kernelWeights, Weights biasWeights) TRTNOEXCEPT = 0; + TRT_DEPRECATED IConvolutionLayer* addConvolution( + ITensor& input, int32_t nbOutputMaps, DimsHW kernelSize, Weights kernelWeights, Weights biasWeights) noexcept + { + return mImpl->addConvolution(input, nbOutputMaps, kernelSize, kernelWeights, biasWeights); + } //! //! \brief Add a fully connected layer to the network. @@ -4976,8 +5510,11 @@ public: //! //! \return The new fully connected layer, or nullptr if it could not be created. //! - virtual IFullyConnectedLayer* addFullyConnected( - ITensor& input, int32_t nbOutputs, Weights kernelWeights, Weights biasWeights) TRTNOEXCEPT = 0; + IFullyConnectedLayer* addFullyConnected( + ITensor& input, int32_t nbOutputs, Weights kernelWeights, Weights biasWeights) noexcept + { + return mImpl->addFullyConnected(input, nbOutputs, kernelWeights, biasWeights); + } //! //! \brief Add an activation layer to the network. @@ -4993,7 +5530,10 @@ public: //! //! \return The new activation layer, or nullptr if it could not be created. //! - virtual IActivationLayer* addActivation(ITensor& input, ActivationType type) TRTNOEXCEPT = 0; + IActivationLayer* addActivation(ITensor& input, ActivationType type) noexcept + { + return mImpl->addActivation(input, type); + } //! //! \brief Add a pooling layer to the network. @@ -5009,8 +5549,10 @@ public: //! //! \deprecated Superseded by addPoolingNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual IPoolingLayer* addPooling( - ITensor& input, PoolingType type, DimsHW windowSize) TRTNOEXCEPT = 0; + TRT_DEPRECATED IPoolingLayer* addPooling(ITensor& input, PoolingType type, DimsHW windowSize) noexcept + { + return mImpl->addPooling(input, type, windowSize); + } //! //! \brief Add a LRN layer to the network. @@ -5026,12 +5568,17 @@ public: //! //! \return The new LRN layer, or nullptr if it could not be created. //! - virtual ILRNLayer* addLRN(ITensor& input, int32_t window, float alpha, float beta, float k) TRTNOEXCEPT = 0; + ILRNLayer* addLRN(ITensor& input, int32_t window, float alpha, float beta, float k) noexcept + { + return mImpl->addLRN(input, window, alpha, beta, k); + } //! //! \brief Add a Scale layer to the network. //! - //! \param input The input tensor to the layer. This tensor is required to have a minimum of 3 dimensions. + //! \param input The input tensor to the layer. + //! This tensor is required to have a minimum of 3 dimensions in implicit batch mode + //! and a minimum of 4 dimensions in explicit batch mode. //! \param mode The scaling mode. //! \param shift The shift value. //! \param scale The scale value. @@ -5048,7 +5595,10 @@ public: //! //! \return The new Scale layer, or nullptr if it could not be created. //! - virtual IScaleLayer* addScale(ITensor& input, ScaleMode mode, Weights shift, Weights scale, Weights power) TRTNOEXCEPT = 0; + IScaleLayer* addScale(ITensor& input, ScaleMode mode, Weights shift, Weights scale, Weights power) noexcept + { + return mImpl->addScale(input, mode, shift, scale, power); + } //! //! \brief Add a SoftMax layer to the network. @@ -5058,7 +5608,10 @@ public: //! //! \return The new SoftMax layer, or nullptr if it could not be created. //! - virtual ISoftMaxLayer* addSoftMax(ITensor& input) TRTNOEXCEPT = 0; + ISoftMaxLayer* addSoftMax(ITensor& input) noexcept + { + return mImpl->addSoftMax(input); + } //! //! \brief Add a concatenation layer to the network. @@ -5070,9 +5623,12 @@ public: //! //! \return The new concatenation layer, or nullptr if it could not be created. //! - //! \warning All tensors must have the same dimensions for all dimensions except for channel. + //! \warning All tensors must have the same dimensions except along the concatenation axis. //! - virtual IConcatenationLayer* addConcatenation(ITensor* const* inputs, int32_t nbInputs) TRTNOEXCEPT = 0; + IConcatenationLayer* addConcatenation(ITensor* const* inputs, int32_t nbInputs) noexcept + { + return mImpl->addConcatenation(inputs, nbInputs); + } //! //! \brief Add a deconvolution layer to the network. @@ -5092,8 +5648,11 @@ public: //! //! \deprecated Superseded by addDeconvolutionNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual IDeconvolutionLayer* addDeconvolution(ITensor& input, int32_t nbOutputMaps, - DimsHW kernelSize, Weights kernelWeights, Weights biasWeights) TRTNOEXCEPT = 0; + TRT_DEPRECATED IDeconvolutionLayer* addDeconvolution( + ITensor& input, int32_t nbOutputMaps, DimsHW kernelSize, Weights kernelWeights, Weights biasWeights) noexcept + { + return mImpl->addDeconvolution(input, nbOutputMaps, kernelSize, kernelWeights, biasWeights); + } //! //! \brief Add an elementwise layer to the network. @@ -5115,89 +5674,10 @@ public: //! //! \return The new elementwise layer, or nullptr if it could not be created. //! - virtual IElementWiseLayer* addElementWise(ITensor& input1, ITensor& input2, ElementWiseOperation op) TRTNOEXCEPT = 0; - - //! - //! \brief Add an \p layerCount deep RNN layer to the network with a - //! sequence length of \p maxSeqLen and \p hiddenSize internal state per - //! layer. - //! - //! \param inputs The input tensor to the layer. - //! \param layerCount The number of layers in the RNN. - //! \param hiddenSize The size of the internal hidden state for each layer. - //! \param maxSeqLen The maximum length of the time sequence. - //! \param op The type of RNN to execute. - //! \param mode The input mode for the RNN. - //! \param dir The direction to run the RNN. - //! \param weights The weights for the weight matrix parameters of the RNN. - //! \param bias The weights for the bias vectors parameters of the RNN. - //! - //! The inputs tensor must be of the type DataType::kFLOAT or DataType::kHALF, - //! and have non-zero volume. - //! - //! See IRNNLayer::setWeights() and IRNNLayer::setBias() for details on the required input - //! format for \p weights and \p bias. - //! - //! The layout for the \p input tensor should be `{1, S_max, N, E}`, where: - //! - `S_max` is the maximum allowed sequence length (number of RNN iterations) - //! - `N` is the batch size - //! - `E` specifies the embedding length (unless ::kSKIP is set, in which case it should match - //! getHiddenSize()). - //! - //! The first output tensor is the output of the final RNN layer across all timesteps, with dimensions - //! `{S_max, N, H}`: - //! - //! - `S_max` is the maximum allowed sequence length (number of RNN iterations) - //! - `N` is the batch size - //! - `H` is an output hidden state (equal to getHiddenSize() or 2x getHiddenSize()) - //! - //! The second tensor is the final hidden state of the RNN across all layers, and if the RNN - //! is an LSTM (i.e. getOperation() is ::kLSTM), then the third tensor is the final cell - //! state of the RNN across all layers. Both the second and third output tensors have dimensions - //! `{L, N, H}`: - //! - //! - `L` is equal to getLayerCount() if getDirection is ::kUNIDIRECTION, - //! and 2*getLayerCount() if getDirection is ::kBIDIRECTION. In the bi-directional - //! case, layer `l`'s final forward hidden state is stored in `L = 2*l`, and - //! final backward hidden state is stored in `L = 2*l + 1`. - //! - `N` is the batch size - //! - `H` is getHiddenSize(). - //! - //! Note that in bidirectional RNNs, the full "hidden state" for a layer `l` - //! is the concatenation of its forward hidden state and its backward hidden - //! state, and its size is 2*H. - //! - //! \deprecated Superseded by addRNNv2 and will be removed in TensorRT 8.0. - //! - //! \see IRNNLayer - //! - //! \warning This layer does not support wildcard dimensions or explicit batch size networks. - //! \warning Int32 tensors are not valid input tensors. - //! - //! \return The new RNN layer, or nullptr if it could not be created. - //! - TRT_DEPRECATED virtual IRNNLayer* addRNN(ITensor& inputs, int32_t layerCount, std::size_t hiddenSize, - int32_t maxSeqLen, RNNOperation op, RNNInputMode mode, RNNDirection dir, Weights weights, - Weights bias) TRTNOEXCEPT = 0; - - //! - //! \brief Add a plugin layer to the network. - //! - //! \param inputs The input tensors to the layer. - //! \param nbInputs The number of input tensors. - //! \param plugin The layer plugin. - //! - //! \see IPluginLayer - //! - //! \deprecated Superseded by addPluginV2 and will be removed in TensorRT 8.0. - //! - //! \warning Plugin inputs do not support wildcard dimensions or explicit batch size networks. - //! \warning Int32 tensors are not valid input tensors. - //! - //! \return the new plugin layer, or nullptr if it could not be created. - //! - TRT_DEPRECATED virtual IPluginLayer* addPlugin( - ITensor* const* inputs, int32_t nbInputs, IPlugin& plugin) TRTNOEXCEPT = 0; + IElementWiseLayer* addElementWise(ITensor& input1, ITensor& input2, ElementWiseOperation op) noexcept + { + return mImpl->addElementWise(input1, input2, op); + } //! //! \brief Add a unary layer to the network. @@ -5213,7 +5693,10 @@ public: //! //! \return The new unary layer, or nullptr if it could not be created //! - virtual IUnaryLayer* addUnary(ITensor& input, UnaryOperation operation) TRTNOEXCEPT = 0; + IUnaryLayer* addUnary(ITensor& input, UnaryOperation operation) noexcept + { + return mImpl->addUnary(input, operation); + } //! \brief Add a padding layer to the network. //! @@ -5227,8 +5710,10 @@ public: //! //! \deprecated Superseded by addPaddingNd and will be removed in TensorRT 9.0. //! - TRT_DEPRECATED virtual IPaddingLayer* addPadding( - ITensor& input, DimsHW prePadding, DimsHW postPadding) TRTNOEXCEPT = 0; + TRT_DEPRECATED IPaddingLayer* addPadding(ITensor& input, DimsHW prePadding, DimsHW postPadding) noexcept + { + return mImpl->addPadding(input, prePadding, postPadding); + } //! //! \brief Add a shuffle layer to the network. @@ -5239,96 +5724,10 @@ public: //! //! \return The new shuffle layer, or nullptr if it could not be created. //! - virtual IShuffleLayer* addShuffle(ITensor& input) TRTNOEXCEPT = 0; - - //! - //! \brief Set the pooling output dimensions formula. - //! - //! \deprecated This method does not currently work reliably and will be removed in TensorRT 8.0. - //! - //! \param formula The formula from computing the pooling output dimensions. If null is passed, the default - //! formula is used. - //! - //! The default formula in each dimension is (inputDim + padding * 2 - kernelSize) / stride + 1. - //! - //! \warning Custom output dimensions formulas are not supported with wildcard dimensions. - //! - //! \see IOutputDimensionsFormula getPoolingOutputDimensionsFormula() - //! - TRT_DEPRECATED virtual void setPoolingOutputDimensionsFormula(IOutputDimensionsFormula* formula) TRTNOEXCEPT = 0; - - //! - //! \brief Get the pooling output dimensions formula. - //! - //! \deprecated This method does not currently work reliably and will be removed in TensorRT 8.0. - //! - //! \return The formula from computing the pooling output dimensions. - //! - //! \warning Custom output dimensions formulas are not supported with wildcard dimensions. - //! - //! \see IOutputDimensionsFormula setPoolingOutputDimensionsFormula() - //! - TRT_DEPRECATED virtual IOutputDimensionsFormula& getPoolingOutputDimensionsFormula() const TRTNOEXCEPT = 0; - - //! - //! \brief Set the convolution output dimensions formula. - //! - //! \deprecated This method does not currently work reliably and will be removed in TensorRT 8.0. - //! - //! \param formula The formula from computing the convolution output dimensions. If null is passed, the default - //! formula is used. - //! - //! The default formula in each dimension is (inputDim + padding * 2 - kernelSize) / stride + 1. - //! - //! \warning Custom output dimensions formulas are not supported with wildcard dimensions. - //! - //! \see IOutputDimensionsFormula getConvolutionOutputDimensionsFormula() - //! - TRT_DEPRECATED virtual void setConvolutionOutputDimensionsFormula( - IOutputDimensionsFormula* formula) TRTNOEXCEPT = 0; - - //! - //! \brief Get the convolution output dimensions formula. - //! - //! \deprecated This method does not currently work reliably and will be removed in TensorRT 8.0. - //! - //! \return The formula from computing the convolution output dimensions. - //! - //! \warning Custom output dimensions formulas are not supported with wildcard dimensions. - //! - //! \see IOutputDimensionsFormula setConvolutionOutputDimensionsFormula() - //! - TRT_DEPRECATED virtual IOutputDimensionsFormula& getConvolutionOutputDimensionsFormula() const TRTNOEXCEPT = 0; - - //! - //! \brief Set the deconvolution output dimensions formula. - //! - //! \deprecated This method does not currently work reliably and will be removed in TensorRT 8.0. - //! - //! \param formula The formula from computing the deconvolution output dimensions. If null is passed, the default! - //! formula is used. - //! - //! The default formula in each dimension is (inputDim - 1) * stride + kernelSize - 2 * padding. - //! - //! \warning Custom output dimensions formulas are not supported with wildcard dimensions. - //! - //! \see IOutputDimensionsFormula getDevonvolutionOutputDimensionsFormula() - //! - TRT_DEPRECATED virtual void setDeconvolutionOutputDimensionsFormula( - IOutputDimensionsFormula* formula) TRTNOEXCEPT = 0; - - //! - //! \brief Get the deconvolution output dimensions formula. - //! - //! \return The formula from computing the deconvolution output dimensions. - //! - //! \deprecated This method does not currently work reliably and will be removed in TensorRT 8.0. - //! - //! \warning Custom output dimensions formulas are not supported with wildcard dimensions. - //! - //! \see IOutputDimensionsFormula setDeconvolutionOutputDimensionsFormula() - //! - TRT_DEPRECATED virtual IOutputDimensionsFormula& getDeconvolutionOutputDimensionsFormula() const TRTNOEXCEPT = 0; + IShuffleLayer* addShuffle(ITensor& input) noexcept + { + return mImpl->addShuffle(input); + } //! //! \brief Get the number of layers in the network. @@ -5337,7 +5736,10 @@ public: //! //! \see getLayer() //! - virtual int32_t getNbLayers() const TRTNOEXCEPT = 0; + int32_t getNbLayers() const noexcept + { + return mImpl->getNbLayers(); + } //! //! \brief Get the layer specified by the given index. @@ -5348,7 +5750,10 @@ public: //! //! \see getNbLayers() //! - virtual ILayer* getLayer(int32_t index) const TRTNOEXCEPT = 0; + ILayer* getLayer(int32_t index) const noexcept + { + return mImpl->getLayer(index); + } //! //! \brief Get the number of inputs in the network. @@ -5357,7 +5762,10 @@ public: //! //! \see getInput() //! - virtual int32_t getNbInputs() const TRTNOEXCEPT = 0; + int32_t getNbInputs() const noexcept + { + return mImpl->getNbInputs(); + } //! //! \brief Get the input tensor specified by the given index. @@ -5366,9 +5774,14 @@ public: //! //! \return The input tensor, or nullptr if the index is out of range. //! + //! \note adding inputs invalidates indexing here + //! //! \see getNbInputs() //! - virtual ITensor* getInput(int32_t index) const TRTNOEXCEPT = 0; // adding inputs invalidates indexing here + ITensor* getInput(int32_t index) const noexcept + { + return mImpl->getInput(index); + } //! //! \brief Get the number of outputs in the network. @@ -5379,7 +5792,10 @@ public: //! //! \see getOutput() //! - virtual int32_t getNbOutputs() const TRTNOEXCEPT = 0; + int32_t getNbOutputs() const noexcept + { + return mImpl->getNbOutputs(); + } //! //! \brief Get the output tensor specified by the given index. @@ -5388,19 +5804,27 @@ public: //! //! \return The output tensor, or nullptr if the index is out of range. //! + //! \note adding inputs invalidates indexing here + //! //! \see getNbOutputs() //! - virtual ITensor* getOutput(int32_t index) const TRTNOEXCEPT = 0; // adding outputs invalidates indexing here + ITensor* getOutput(int32_t index) const noexcept + { + return mImpl->getOutput(index); + } //! //! \brief Destroy this INetworkDefinition object. //! - virtual void destroy() TRTNOEXCEPT = 0; + //! \deprecated Deprecated interface will be removed in TensorRT 10.0. + //! + //! \warning Calling destroy on a managed pointer will result in a double-free error. + //! + TRT_DEPRECATED void destroy() noexcept + { + delete this; + } -protected: - virtual ~INetworkDefinition() {} - -public: //! //! \brief Add a reduce layer to the network. //! @@ -5424,7 +5848,11 @@ public: //! //! \return The new reduce layer, or nullptr if it could not be created. //! - virtual IReduceLayer* addReduce(ITensor& input, ReduceOperation operation, uint32_t reduceAxes, bool keepDimensions) TRTNOEXCEPT = 0; + IReduceLayer* addReduce( + ITensor& input, ReduceOperation operation, uint32_t reduceAxes, bool keepDimensions) noexcept + { + return mImpl->addReduce(input, operation, reduceAxes, keepDimensions); + } //! //! \brief Add a TopK layer to the network. @@ -5454,7 +5882,10 @@ public: //! //! \return The new TopK layer, or nullptr if it could not be created. //! - virtual ITopKLayer* addTopK(ITensor& input, TopKOperation op, int32_t k, uint32_t reduceAxes) TRTNOEXCEPT = 0; + ITopKLayer* addTopK(ITensor& input, TopKOperation op, int32_t k, uint32_t reduceAxes) noexcept + { + return mImpl->addTopK(input, op, k, reduceAxes); + } //! //! \brief Add a gather layer to the network. @@ -5467,7 +5898,10 @@ public: //! //! \return The new gather layer, or nullptr if it could not be created. //! - virtual IGatherLayer* addGather(ITensor& data, ITensor& indices, int32_t axis) TRTNOEXCEPT = 0; + IGatherLayer* addGather(ITensor& data, ITensor& indices, int32_t axis) noexcept + { + return mImpl->addGather(data, indices, axis); + } //! //! \brief Add a RaggedSoftMax layer to the network. @@ -5482,7 +5916,10 @@ public: //! //! \return The new RaggedSoftMax layer, or nullptr if it could not be created. //! - virtual IRaggedSoftMaxLayer* addRaggedSoftMax(ITensor& input, ITensor& bounds) TRTNOEXCEPT = 0; + IRaggedSoftMaxLayer* addRaggedSoftMax(ITensor& input, ITensor& bounds) noexcept + { + return mImpl->addRaggedSoftMax(input, bounds); + } //! //! \brief Add a MatrixMultiply layer to the network. @@ -5498,28 +5935,11 @@ public: //! //! \return The new matrix multiply layer, or nullptr if it could not be created. //! - virtual IMatrixMultiplyLayer* addMatrixMultiply( - ITensor& input0, MatrixOperation op0, ITensor& input1, MatrixOperation op1) TRTNOEXCEPT = 0; - - //! - //! \brief Add a MatrixMultiply layer to the network. - //! - //! \param input0 The first input tensor (commonly A). - //! \param transpose0 If true, op(input0)=transpose(input0), else op(input0)=input0. - //! \param input1 The second input tensor (commonly B). - //! \param transpose1 If true, op(input1)=transpose(input1), else op(input1)=input1. - //! - //! \see IMatrixMultiplyLayer - //! - //! \return The new matrix multiply layer, or nullptr if it could not be created. - //! - //! \warning Int32 tensors are not valid input tensors. - //! - //! \deprecated This interface is superseded by the overload that replaces bool with MatrixOperation and will be - //! removed in TensorRT 8.0. - //! - TRT_DEPRECATED virtual IMatrixMultiplyLayer* addMatrixMultiply( - ITensor& input0, bool transpose0, ITensor& input1, bool transpose1) TRTNOEXCEPT = 0; + IMatrixMultiplyLayer* addMatrixMultiply( + ITensor& input0, MatrixOperation op0, ITensor& input1, MatrixOperation op1) noexcept + { + return mImpl->addMatrixMultiply(input0, op0, input1, op1); + } //! //! \brief Add a constant layer to the network. @@ -5541,7 +5961,10 @@ public: //! If a wildcard dimension is used, the volume of the runtime dimensions must equal //! the number of weights specified. //! - virtual IConstantLayer* addConstant(Dims dimensions, Weights weights) TRTNOEXCEPT = 0; + IConstantLayer* addConstant(Dims dimensions, Weights weights) noexcept + { + return mImpl->addConstant(dimensions, weights); + } //! //! \brief Add an \p layerCount deep RNN layer to the network with \p hiddenSize internal states that can @@ -5599,34 +6022,18 @@ public: //! //! \see IRNNv2Layer //! - //! \deprecated Superseded by ILoop::addLoop and will be removed in TensorRT 9.0. + //! \deprecated Superseded by INetworkDefinition::addLoop and will be removed in TensorRT 9.0. //! //! \warning RNN inputs do not support wildcard dimensions or explicit batch size networks. //! \warning Int32 tensors are not valid input tensors, only for sequence lengths. //! //! \return The new RNN layer, or nullptr if it could not be created. //! - TRT_DEPRECATED virtual IRNNv2Layer* addRNNv2( - ITensor& input, int32_t layerCount, int32_t hiddenSize, int32_t maxSeqLen, RNNOperation op) TRTNOEXCEPT = 0; - - //! - //! \brief Add a plugin layer to the network using an IPluginExt interface. - //! - //! \param inputs The input tensors to the layer. - //! \param nbInputs The number of input tensors. - //! \param plugin The layer plugin. - //! - //! \see IPluginLayer - //! - //! \deprecated Superseded by addPluginV2 and will be removed in TensorRT 8.0. - //! - //! \warning Plugin inputs do not support wildcard dimensions or explicit batch size networks. - //! \warning Int32 tensors are not valid input tensors. - //! - //! \return The new plugin layer, or nullptr if it could not be created. - //! - TRT_DEPRECATED virtual IPluginLayer* addPluginExt( - ITensor* const* inputs, int32_t nbInputs, IPluginExt& plugin) TRTNOEXCEPT = 0; + TRT_DEPRECATED IRNNv2Layer* addRNNv2( + ITensor& input, int32_t layerCount, int32_t hiddenSize, int32_t maxSeqLen, RNNOperation op) noexcept + { + return mImpl->addRNNv2(input, layerCount, hiddenSize, maxSeqLen, op); + } //! //! \brief Add an identity layer. @@ -5639,7 +6046,10 @@ public: //! //! \return The new identity layer, or nullptr if it could not be created. //! - virtual IIdentityLayer* addIdentity(ITensor& input) TRTNOEXCEPT = 0; + IIdentityLayer* addIdentity(ITensor& input) noexcept + { + return mImpl->addIdentity(input); + } //! //! \brief remove a tensor from the network definition. @@ -5651,7 +6061,10 @@ public: //! and the call will be ignored. Its intended use is to remove detached tensors after //! e.g. concatenating two networks with Layer::setInput(). //! - virtual void removeTensor(ITensor& tensor) TRTNOEXCEPT = 0; + void removeTensor(ITensor& tensor) noexcept + { + mImpl->removeTensor(tensor); + } //! //! \brief unmark a tensor as a network output. @@ -5660,7 +6073,10 @@ public: //! //! see markOutput() //! - virtual void unmarkOutput(ITensor& tensor) TRTNOEXCEPT = 0; + void unmarkOutput(ITensor& tensor) noexcept + { + mImpl->unmarkOutput(tensor); + } //! //! \brief Add a plugin layer to the network using the IPluginV2 interface. @@ -5676,7 +6092,10 @@ public: //! //! \return The new plugin layer, or nullptr if it could not be created. //! - virtual IPluginV2Layer* addPluginV2(ITensor* const* inputs, int32_t nbInputs, IPluginV2& plugin) TRTNOEXCEPT = 0; + IPluginV2Layer* addPluginV2(ITensor* const* inputs, int32_t nbInputs, IPluginV2& plugin) noexcept + { + return mImpl->addPluginV2(inputs, nbInputs, plugin); + } //! //! \brief Add a slice layer to the network. @@ -5692,7 +6111,10 @@ public: //! //! \return The new slice layer, or nullptr if it could not be created. //! - virtual ISliceLayer* addSlice(ITensor& input, Dims start, Dims size, Dims stride) TRTNOEXCEPT = 0; + ISliceLayer* addSlice(ITensor& input, Dims start, Dims size, Dims stride) noexcept + { + return mImpl->addSlice(input, start, size, stride); + } //! //! \brief Sets the name of the network. @@ -5711,7 +6133,10 @@ public: //! //! \return none //! - virtual void setName(const char* name) TRTNOEXCEPT = 0; + void setName(const char* name) noexcept + { + mImpl->setName(name); + } //! //! \brief Returns the name associated with the network. @@ -5722,7 +6147,10 @@ public: //! //! \return A zero delimited C-style string representing the name of the network. //! - virtual const char* getName() const TRTNOEXCEPT = 0; + const char* getName() const noexcept + { + return mImpl->getName(); + } //! //! \brief Add a shape layer to the network. @@ -5737,7 +6165,10 @@ public: //! //! \return The new shape layer, or nullptr if it could not be created. //! - virtual IShapeLayer* addShape(ITensor& input) TRTNOEXCEPT = 0; + IShapeLayer* addShape(ITensor& input) noexcept + { + return mImpl->addShape(input); + } //! //! \brief Query whether the network was created with an implicit batch dimension. @@ -5753,7 +6184,10 @@ public: //! //! \see createNetworkV2 //! - virtual bool hasImplicitBatchDimension() const TRTNOEXCEPT = 0; + bool hasImplicitBatchDimension() const noexcept + { + return mImpl->hasImplicitBatchDimension(); + } //! //! \brief Enable tensor's value to be computed by IExecutionContext::getShapeBinding. @@ -5768,7 +6202,10 @@ public: //! //! \see isShapeBinding(), getShapeBinding() //! - virtual bool markOutputForShapes(ITensor& tensor) TRTNOEXCEPT = 0; + bool markOutputForShapes(ITensor& tensor) noexcept + { + return mImpl->markOutputForShapes(tensor); + } //! //! \brief Undo markOutputForShapes. @@ -5777,7 +6214,10 @@ public: //! //! \return True if successful, false if tensor is not marked as an output. //! - virtual bool unmarkOutputForShapes(ITensor& tensor) TRTNOEXCEPT = 0; + bool unmarkOutputForShapes(ITensor& tensor) noexcept + { + return mImpl->unmarkOutputForShapes(tensor); + } //! //! \brief Add a parametric ReLU layer to the network. @@ -5792,7 +6232,10 @@ public: //! //! \return The new parametric ReLU layer, or nullptr if it could not be created. //! - virtual IParametricReLULayer* addParametricReLU(ITensor& input, ITensor& slope) noexcept = 0; + IParametricReLULayer* addParametricReLU(ITensor& input, ITensor& slope) noexcept + { + return mImpl->addParametricReLU(input, slope); + } //! //! \brief Add a multi-dimension convolution layer to the network. @@ -5811,8 +6254,11 @@ public: //! //! \return The new convolution layer, or nullptr if it could not be created. //! - virtual IConvolutionLayer* addConvolutionNd(ITensor& input, int32_t nbOutputMaps, Dims kernelSize, - Weights kernelWeights, Weights biasWeights) TRTNOEXCEPT = 0; + IConvolutionLayer* addConvolutionNd( + ITensor& input, int32_t nbOutputMaps, Dims kernelSize, Weights kernelWeights, Weights biasWeights) noexcept + { + return mImpl->addConvolutionNd(input, nbOutputMaps, kernelSize, kernelWeights, biasWeights); + } //! //! \brief Add a multi-dimension pooling layer to the network. @@ -5828,7 +6274,10 @@ public: //! //! \return The new pooling layer, or nullptr if it could not be created. //! - virtual IPoolingLayer* addPoolingNd(ITensor& input, PoolingType type, Dims windowSize) TRTNOEXCEPT = 0; + IPoolingLayer* addPoolingNd(ITensor& input, PoolingType type, Dims windowSize) noexcept + { + return mImpl->addPoolingNd(input, type, windowSize); + } //! //! \brief Add a multi-dimension deconvolution layer to the network. @@ -5847,8 +6296,11 @@ public: // //! \return The new deconvolution layer, or nullptr if it could not be created. //! - virtual IDeconvolutionLayer* addDeconvolutionNd(ITensor& input, int32_t nbOutputMaps, Dims kernelSize, - Weights kernelWeights, Weights biasWeights) TRTNOEXCEPT = 0; + IDeconvolutionLayer* addDeconvolutionNd( + ITensor& input, int32_t nbOutputMaps, Dims kernelSize, Weights kernelWeights, Weights biasWeights) noexcept + { + return mImpl->addDeconvolutionNd(input, nbOutputMaps, kernelSize, kernelWeights, biasWeights); + } //! //! \brief Add a multi-dimension scale layer to the network. @@ -5870,14 +6322,21 @@ public: //! For ::kCHANNEL, the number of weights is C. //! For ::kELEMENTWISE, the number of weights is C*D*E*F. //! + //! channelAxis can also be set explicitly using setChannelAxis(). + //! //! \see IScaleLayer + //! \see setChannelAxis() + //! //! \warning Int32 tensors are not valid input tensors. //! \warning Only 2D or 3D scale is supported. //! //! \return The new Scale layer, or nullptr if it could not be created. //! - virtual IScaleLayer* addScaleNd(ITensor& input, ScaleMode mode, Weights shift, Weights scale, Weights power, - int32_t channelAxis) TRTNOEXCEPT = 0; + IScaleLayer* addScaleNd( + ITensor& input, ScaleMode mode, Weights shift, Weights scale, Weights power, int32_t channelAxis) noexcept + { + return mImpl->addScaleNd(input, mode, shift, scale, power, channelAxis); + } //! \brief Add a resize layer to the network. //! @@ -5889,11 +6348,16 @@ public: //! //! \return The new resize layer, or nullptr if it could not be created. //! - virtual IResizeLayer* addResize(ITensor& input) TRTNOEXCEPT = 0; + IResizeLayer* addResize(ITensor& input) noexcept + { + return mImpl->addResize(input); + } //! //! \brief True if network is an explicit precision network //! + //! \deprecated Will be removed in TensorRT 10.0. + //! //! hasExplicitPrecision() is true if and only if this INetworkDefinition //! was created with createNetworkV2() with NetworkDefinitionCreationFlag::kEXPLICIT_PRECISION set. //! @@ -5901,7 +6365,10 @@ public: //! //! \return True if network has explicit precision, false otherwise. //! - virtual bool hasExplicitPrecision() const TRTNOEXCEPT = 0; + TRT_DEPRECATED bool hasExplicitPrecision() const noexcept + { + return mImpl->hasExplicitPrecision(); + } //! //! \brief Add a loop to the network. @@ -5912,7 +6379,12 @@ public: //! or nullptr if network has an implicit batch dimension or this version //! of TensorRT does not support loops. //! - virtual ILoop* addLoop() noexcept = 0; + //! The network must not have an implicit batch dimension. + //! + ILoop* addLoop() noexcept + { + return mImpl->addLoop(); + } //! \brief Add a select layer to the network. //! @@ -5942,22 +6414,33 @@ public: //! //! then the output dimensions are [1,3,0,9]. //! + //! The network must not have an implicit batch dimension. + //! //! \see ISelectLayer //! //! \return The new select layer, or nullptr if it could not be created. - virtual ISelectLayer* addSelect(ITensor& condition, ITensor& thenInput, ITensor& elseInput) TRTNOEXCEPT = 0; + ISelectLayer* addSelect(ITensor& condition, ITensor& thenInput, ITensor& elseInput) noexcept + { + return mImpl->addSelect(condition, thenInput, elseInput); + } //! \brief Add a fill layer to the network. //! //! \param dimensions The output tensor dimensions. //! \param op The fill operation that the layer applies. //! - //! \warning The dimensions's nbDims must be 1. + //! \warning For FillOperation::kLINSPACE, dimensions.nbDims must be 1. + //! + //! The network must not have an implicit batch dimension. //! //! \see IFillLayer //! //! \return The new fill layer, or nullptr if it could not be created. - virtual IFillLayer* addFill(Dims dimensions, FillOperation op) noexcept = 0; + //! + IFillLayer* addFill(Dims dimensions, FillOperation op) noexcept + { + return mImpl->addFill(dimensions, op); + } //! \brief Add a padding layer to the network. Only 2D padding is currently supported. //! @@ -5969,8 +6452,105 @@ public: //! //! \return The new padding layer, or nullptr if it could not be created. //! - virtual IPaddingLayer* addPaddingNd( - ITensor& input, Dims prePadding, Dims postPadding) TRTNOEXCEPT = 0; + IPaddingLayer* addPaddingNd(ITensor& input, Dims prePadding, Dims postPadding) noexcept + { + return mImpl->addPaddingNd(input, prePadding, postPadding); + } + + //! \brief Associate a name with all current uses of the given weights. + //! + //! The name must be set after the Weights are used in the network. + //! Lookup is associative. The name applies to all Weights with matching + //! type, value pointer, and count. If Weights with a matching value + //! pointer, but different type or count exists in the network, an + //! error message is issued, the name is rejected, and return false. + //! If the name has already been used for other weights, + //! return false. A nullptr causes the weights to become unnamed, + //! i.e. clears any previous name. + //! + //! \param weights The weights to be named. + //! \param name The name to associate with the weights. + //! + //! \return true on success. + bool setWeightsName(Weights weights, const char* name) noexcept + { + return mImpl->setWeightsName(weights, name); + } + + //! + //! \brief Set the ErrorRecorder for this interface + //! + //! Assigns the ErrorRecorder to this interface. The ErrorRecorder will track all errors during execution. + //! This function will call incRefCount of the registered ErrorRecorder at least once. Setting + //! recorder to nullptr unregisters the recorder with the interface, resulting in a call to decRefCount if + //! a recorder has been registered. + //! + //! If an error recorder is not set, messages will be sent to the global log stream. + //! + //! \param recorder The error recorder to register with this interface. + // + //! \see getErrorRecorder() + //! + void setErrorRecorder(IErrorRecorder* recorder) noexcept + { + mImpl->setErrorRecorder(recorder); + } + + //! + //! \brief get the ErrorRecorder assigned to this interface. + //! + //! Retrieves the assigned error recorder object for the given class. + //! A nullptr will be returned if setErrorRecorder has not been called. + //! + //! \return A pointer to the IErrorRecorder object that has been registered. + //! + //! \see setErrorRecorder() + //! + IErrorRecorder* getErrorRecorder() const noexcept + { + return mImpl->getErrorRecorder(); + } + + //! + //! \brief Add a dequantization layer to the network. + //! + //! \param input The input tensor to be quantized. + //! \param scale A tensor with the scale value. + //! + //! \see IDequantizeLayer + //! + //! \p input tensor data type must be DataType::kFLOAT. + //! \p scale tensor data type must be DataType::kFLOAT. The subgraph which terminates with the \p scale tensor must + //! be a build-time constant. + //! + //! \return The new quantization layer, or nullptr if it could not be created. + //! + IDequantizeLayer* addDequantize(ITensor& input, ITensor& scale) noexcept + { + return mImpl->addDequantize(input, scale); + } + + //! + //! \brief Add a quantization layer to the network. + //! + //! \param input The input tensor to be quantized. + //! \param scale A tensor with the scale value. + //! + //! \see IQuantizeLayer + //! + //! \p input tensor data type must be DataType::kFLOAT. + //! \p scale tensor data type must be DataType::kFLOAT. The subgraph which terminates with the \p scale tensor must + //! be a build-time constant. + //! + //! \return The new quantization layer, or nullptr if it could not be created. + //! + IQuantizeLayer* addQuantize(ITensor& input, ITensor& scale) noexcept + { + return mImpl->addQuantize(input, scale); + } + +protected: + apiv::VNetworkDefinition* mImpl; }; //! @@ -5988,7 +6568,7 @@ enum class CalibrationAlgoType : int32_t //! Maximum number of elements in CalibrationAlgoType enum. \see DataType template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 4; } @@ -6012,7 +6592,7 @@ public: //! //! \return The batch size. //! - virtual int32_t getBatchSize() const TRTNOEXCEPT = 0; + virtual int32_t getBatchSize() const noexcept = 0; //! //! \brief Get a batch of input for calibration. @@ -6027,7 +6607,7 @@ public: //! //! \see getBatchSize() //! - virtual bool getBatch(void* bindings[], const char* names[], int32_t nbBindings) TRTNOEXCEPT = 0; + virtual bool getBatch(void* bindings[], const char* names[], int32_t nbBindings) noexcept = 0; //! //! \brief Load a calibration cache. @@ -6043,7 +6623,7 @@ public: //! //! \return A pointer to the cache, or nullptr if there is no data. //! - virtual const void* readCalibrationCache(std::size_t& length) TRTNOEXCEPT = 0; + virtual const void* readCalibrationCache(std::size_t& length) noexcept = 0; //! //! \brief Save a calibration cache. @@ -6053,16 +6633,16 @@ public: //! //! \see readCalibrationCache() //! - virtual void writeCalibrationCache(const void* ptr, std::size_t length) TRTNOEXCEPT = 0; + virtual void writeCalibrationCache(const void* ptr, std::size_t length) noexcept = 0; //! //! \brief Get the algorithm used by this calibrator. //! //! \return The algorithm used by the calibrator. //! - virtual CalibrationAlgoType getAlgorithm() TRTNOEXCEPT = 0; + virtual CalibrationAlgoType getAlgorithm() noexcept = 0; - virtual ~IInt8Calibrator() {} + virtual ~IInt8Calibrator() noexcept = default; }; //! @@ -6075,9 +6655,12 @@ public: //! //! Signal that this is the entropy calibrator. //! - CalibrationAlgoType getAlgorithm() TRTNOEXCEPT override { return CalibrationAlgoType::kENTROPY_CALIBRATION; } + CalibrationAlgoType getAlgorithm() noexcept override + { + return CalibrationAlgoType::kENTROPY_CALIBRATION; + } - virtual ~IInt8EntropyCalibrator() {} + virtual ~IInt8EntropyCalibrator() noexcept = default; }; //! @@ -6090,14 +6673,16 @@ public: //! //! Signal that this is the entropy calibrator 2. //! - CalibrationAlgoType getAlgorithm() TRTNOEXCEPT override { return CalibrationAlgoType::kENTROPY_CALIBRATION_2; } + CalibrationAlgoType getAlgorithm() noexcept override + { + return CalibrationAlgoType::kENTROPY_CALIBRATION_2; + } - virtual ~IInt8EntropyCalibrator2() {} + virtual ~IInt8EntropyCalibrator2() noexcept = default; }; //! -//! MinMax Calibrator. This is the preferred calibrator for NLP tasks. It supports per -//! activation tensor scaling. +//! MinMax Calibrator. It supports per activation tensor scaling. //! class IInt8MinMaxCalibrator : public IInt8Calibrator { @@ -6105,9 +6690,12 @@ public: //! //! Signal that this is the MinMax Calibrator. //! - CalibrationAlgoType getAlgorithm() TRTNOEXCEPT override { return CalibrationAlgoType::kMINMAX_CALIBRATION; } + CalibrationAlgoType getAlgorithm() noexcept override + { + return CalibrationAlgoType::kMINMAX_CALIBRATION; + } - virtual ~IInt8MinMaxCalibrator() {} + virtual ~IInt8MinMaxCalibrator() noexcept = default; }; //! @@ -6120,7 +6708,10 @@ public: //! //! Signal that this is the legacy calibrator. //! - CalibrationAlgoType getAlgorithm() TRTNOEXCEPT override { return CalibrationAlgoType::kLEGACY_CALIBRATION; } + CalibrationAlgoType getAlgorithm() noexcept override + { + return CalibrationAlgoType::kLEGACY_CALIBRATION; + } //! //! \brief The quantile (between 0 and 1) that will be used to select the region maximum when the quantile method @@ -6128,7 +6719,7 @@ public: //! //! See the user guide for more details on how the quantile is used. //! - virtual double getQuantile() const TRTNOEXCEPT = 0; + virtual double getQuantile() const noexcept = 0; //! //! \brief The fraction (between 0 and 1) of the maximum used to define the regression cutoff when using regression @@ -6136,7 +6727,7 @@ public: //! //! See the user guide for more details on how the regression cutoff is used //! - virtual double getRegressionCutoff() const TRTNOEXCEPT = 0; + virtual double getRegressionCutoff() const noexcept = 0; //! //! \brief Load a histogram. @@ -6150,7 +6741,7 @@ public: //! //! \return A pointer to the cache, or nullptr if there is no data. //! - virtual const void* readHistogramCache(std::size_t& length) TRTNOEXCEPT = 0; + virtual const void* readHistogramCache(std::size_t& length) noexcept = 0; //! //! \brief Save a histogram cache. @@ -6160,9 +6751,9 @@ public: //! //! \see readHistogramCache() //! - virtual void writeHistogramCache(const void* ptr, std::size_t length) TRTNOEXCEPT = 0; + virtual void writeHistogramCache(const void* ptr, std::size_t length) noexcept = 0; - virtual ~IInt8LegacyCalibrator() {} + virtual ~IInt8LegacyCalibrator() noexcept = default; }; //! @@ -6175,26 +6766,36 @@ public: //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. //! -class IAlgorithmIOInfo +class IAlgorithmIOInfo : public INoCopy { public: //! //! \brief Return TensorFormat of the input/output of algorithm. //! - virtual TensorFormat getTensorFormat() const TRTNOEXCEPT = 0; + TensorFormat getTensorFormat() const noexcept + { + return mImpl->getTensorFormat(); + } //! //! \brief Return DataType of the input/output of algorithm. //! - virtual DataType getDataType() const TRTNOEXCEPT = 0; + DataType getDataType() const noexcept + { + return mImpl->getDataType(); + } //! //! \brief Return strides of the input/output tensor of algorithm. //! - virtual Dims getStrides() const TRTNOEXCEPT = 0; + Dims getStrides() const noexcept + { + return mImpl->getStrides(); + } protected: - virtual ~IAlgorithmIOInfo() {} + virtual ~IAlgorithmIOInfo() noexcept = default; + apiv::VAlgorithmIOInfo* mImpl; }; //! @@ -6208,21 +6809,28 @@ protected: //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. //! -class IAlgorithmVariant +class IAlgorithmVariant : public INoCopy { public: //! //! \brief Return implementation of the algorithm. //! - virtual int64_t getImplementation() const TRTNOEXCEPT = 0; + int64_t getImplementation() const noexcept + { + return mImpl->getImplementation(); + } //! //! \brief Return tactic of the algorithm. //! - virtual int64_t getTactic() const TRTNOEXCEPT = 0; + int64_t getTactic() const noexcept + { + return mImpl->getTactic(); + } protected: - virtual ~IAlgorithmVariant() {} + virtual ~IAlgorithmVariant() noexcept = default; + apiv::VAlgorithmVariant* mImpl; }; //! @@ -6233,14 +6841,17 @@ protected: //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. //! -class IAlgorithmContext +class IAlgorithmContext : public INoCopy { public: //! //! \brief Return name of the algorithm node. //! This is a unique identifier for the IAlgorithmContext. //! - virtual const char* getName() const TRTNOEXCEPT = 0; + const char* getName() const noexcept + { + return mImpl->getName(); + } //! //! \brief Get the minimum / optimum / maximum dimensions for input or output tensor. @@ -6248,20 +6859,30 @@ public: //! and the outputs. //! \param select Which of the minimum, optimum, or maximum dimensions to be queried. //! - virtual Dims getDimensions(int32_t index, OptProfileSelector select) const TRTNOEXCEPT = 0; + Dims getDimensions(int32_t index, OptProfileSelector select) const noexcept + { + return mImpl->getDimensions(index, select); + } //! //! \brief Return number of inputs of the algorithm. //! - virtual int32_t getNbInputs() const TRTNOEXCEPT = 0; + int32_t getNbInputs() const noexcept + { + return mImpl->getNbInputs(); + } //! //! \brief Return number of outputs of the algorithm. //! - virtual int32_t getNbOutputs() const TRTNOEXCEPT = 0; + int32_t getNbOutputs() const noexcept + { + return mImpl->getNbOutputs(); + } protected: - virtual ~IAlgorithmContext() {} + virtual ~IAlgorithmContext() noexcept = default; + apiv::VAlgorithmContext* mImpl; }; //! @@ -6273,7 +6894,7 @@ protected: //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. //! -class IAlgorithm +class IAlgorithm : public INoCopy { public: //! @@ -6282,26 +6903,56 @@ public: //! \param index Index of the input or output of the algorithm. Incremental numbers assigned to indices of inputs //! and the outputs. //! - virtual const IAlgorithmIOInfo& getAlgorithmIOInfo(int32_t index) const TRTNOEXCEPT = 0; + //! \return a reference to IAlgorithmIOInfo specified by index or the first algorithm if index is out of range. + //! + //! \deprecated API will be removed in TensorRT 10.0, use IAlgorithm::getAlgorithmIOInfoByIndex instead. + //! + TRT_DEPRECATED const IAlgorithmIOInfo& getAlgorithmIOInfo(int32_t index) const noexcept + { + return mImpl->getAlgorithmIOInfo(index); + } //! //! \brief Returns the algorithm variant. //! - virtual const IAlgorithmVariant& getAlgorithmVariant() const TRTNOEXCEPT = 0; + const IAlgorithmVariant& getAlgorithmVariant() const noexcept + { + return mImpl->getAlgorithmVariant(); + } //! //! \brief The time in milliseconds to execute the algorithm. //! - virtual float getTimingMSec() const TRTNOEXCEPT = 0; + float getTimingMSec() const noexcept + { + return mImpl->getTimingMSec(); + } //! //! \brief The size of the GPU temporary memory in bytes which the algorithm uses at execution time. //! - virtual std::size_t getWorkspaceSize() const TRTNOEXCEPT = 0; + std::size_t getWorkspaceSize() const noexcept + { + return mImpl->getWorkspaceSize(); + } + + //! + //! \brief Returns the format of an Algorithm input or output. Algorithm inputs are incrementally numbered first, + //! followed by algorithm outputs. + //! \param index Index of the input or output of the algorithm. Incremental numbers assigned to indices of inputs + //! and the outputs. + //! + //! \return a pointer to a IAlgorithmIOInfo interface or nullptr if index is out of range. + //! + const IAlgorithmIOInfo* getAlgorithmIOInfoByIndex(int32_t index) const noexcept + { + return mImpl->getAlgorithmIOInfoByIndex(index); + } protected: - virtual ~IAlgorithm() {} -}; + virtual ~IAlgorithm() noexcept = default; + apiv::VAlgorithm* mImpl; +}; // IAlgorithm //! //! \class IAlgorithmSelector @@ -6311,7 +6962,7 @@ protected: //! \note A layer in context of algorithm selection may be different from ILayer in INetworkDefiniton. //! For example, an algorithm might be implementing a conglomeration of multiple ILayers in INetworkDefinition. //! -class IAlgorithmSelector +class IAlgorithmSelector { public: //! @@ -6323,12 +6974,13 @@ public: //! \param nbChoices Number of algorithm choices. //! \param selection The user writes indices of selected choices in to selection buffer which is of size nbChoices. //! - //! \note TRT uses its default algorithm selection to choose from the list provided. - //! If return value is 0, TRT’s default algorithm selection is used unless strict type constraints are set. - //! The list of choices is valid only for this specific algorithm context. + //! \note TensorRT uses its default algorithm selection to choose from the list provided. + //! If return value is 0, TensorRT’s default algorithm selection is used unless strict type constraints are + //! set. The list of choices is valid only for this specific algorithm context. //! virtual int32_t selectAlgorithms(const IAlgorithmContext& context, const IAlgorithm* const* choices, - int32_t nbChoices, int32_t* selection) TRTNOEXCEPT = 0; + int32_t nbChoices, int32_t* selection) noexcept + = 0; //! //! \brief Called by TensorRT to report choices it made. //! @@ -6340,18 +6992,19 @@ public: //! \param nbAlgorithms The size of algoContexts as well as algoChoices. //! virtual void reportAlgorithms(const IAlgorithmContext* const* algoContexts, const IAlgorithm* const* algoChoices, - int32_t nbAlgorithms) TRTNOEXCEPT = 0; + int32_t nbAlgorithms) noexcept + = 0; - virtual ~IAlgorithmSelector() {} + virtual ~IAlgorithmSelector() noexcept = default; }; //! -//! \brief Represents a collection of one or more QuantizationFlag values using binary OR +//! \brief Represents one or more QuantizationFlag values using binary OR //! operations. //! //! \see IBuilderConfig::getQuantizationFlags(), IBuilderConfig::setQuantizationFlags() //! -typedef uint32_t QuantizationFlags; +using QuantizationFlags = uint32_t; //! //! \enum QuantizationFlag @@ -6370,18 +7023,18 @@ enum class QuantizationFlag : int32_t //! Maximum number of quantization flags in QuantizationFlag enum. \see QuantizationFlag template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 1; } //! -//! \brief Represents a collection of one or more QuantizationFlag values using binary OR +//! \brief Represents one or more QuantizationFlag values using binary OR //! operations, e.g., 1U << BuilderFlag::kFP16 | 1U << BuilderFlag::kDEBUG. //! //! \see IBuilderConfig::getFlags(), ITensor::setFlags(), //! -typedef uint32_t BuilderFlags; +using BuilderFlags = uint32_t; //! //! \enum BuilderFlag @@ -6403,14 +7056,24 @@ enum class BuilderFlag : int32_t //! Allow (but not require) computations on tensors of type DataType::kFLOAT to use TF32. //! TF32 computes inner products by rounding the inputs to 10-bit mantissas before //! multiplying, but accumulates the sum using 23-bit mantissas. Enabled by default. - kTF32 = 7 + kTF32 = 7, + + //! Allow the builder to examine weights and use optimized functions when weights have suitable sparsity. + kSPARSE_WEIGHTS = 8, + + //! Change the allowed parameters in the EngineCapability::kSTANDARD flow to + //! match the restrictions that EngineCapability::kSAFETY check against for DeviceType::kGPU + //! and EngineCapability::kDLA_STANDALONE check against the DeviceType::kDLA case. This flag + //! is forced to true if EngineCapability::kSAFETY at build time if it is unset. + //! + kSAFETY_SCOPE = 9 }; //! Maximum number of builder flags in BuilderFlag enum. \see BuilderFlag template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { - return 8; + return 10; } //! @@ -6430,49 +7093,108 @@ enum class ProfilingVerbosity : int32_t //! Maximum number of profile verbosity levels in ProfilingVerbosity enum. \see ProfilingVerbosity template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 3; } - //! -//! \enum TacticSource +//! \class ITimingCache //! -//! \brief List of tactic sources for TensorRT. +//! \brief Class to handle tactic timing info collected from builder. //! -//! \see TacticSources, IBuilderConfig::setTacticSources(), IBuilderConfig::getTacticSources() +//! The timing cache is created or initialized by IBuilderConfig. It can be shared across builder instances +//! to accelerate the builder wallclock time. //! -enum class TacticSource : int32_t +//! \see IBuilderConfig +//! +//! +//! \class ITimingCache +//! +//! \brief Class to handle tactic timing info collected from builder. +//! +//! The timing cache is created or initialized by IBuilderConfig. It can be shared across builder instances +//! to accelerate the builder wallclock time. +//! +//! \see IBuilderConfig +//! +//! +//! \class ITimingCache +//! +//! \brief Class to handle tactic timing info collected from builder. +//! +//! The timing cache is created or initialized by IBuilderConfig. It can be shared across builder instances +//! to accelerate the builder wallclock time. +//! +//! \see IBuilderConfig +//! +class ITimingCache : public INoCopy { - //! \note Disabling kCUBLAS will cause the cublas handle passed to plugins in attachToContext to be null. - kCUBLAS = 0, //!< cuBLAS tactics. - kCUBLAS_LT = 1 //!< cuBLAS LT tactics +public: + virtual ~ITimingCache() noexcept = default; + + //! + //! \brief Serialize a timing cache to IHostMemory object. + //! + //! This function allows serialization of current timing cache. + //! + //! \return A pointer to a IHostMemory object that contains a serialized timing cache. + //! + //! \see IHostMemory + //! + nvinfer1::IHostMemory* serialize() const noexcept + { + return mImpl->serialize(); + } + + //! + //! \brief Combine input timing cache into local instance. + //! + //! This function allows combining entries in the input timing cache to local cache object. + //! + //! \param inputCache The input timing cache. + //! \param ignoreMismatch Whether or not to allow cache verification header mismatch. + //! + //! \return True if combined successfully, false otherwise. + //! + //! Append entries in input cache to local cache. Conflicting entries will be skipped + //! The input cache must be generated by a TensorRT build of exact same version, otherwise + //! combine will be skipped and return false. + //! ignoreMismatch must be set to true if combining a timing cache created from a + //! different device. + //! + //! \warning Combining caches generated from devices with different device properties may + //! lead to functional/performance bugs! + //! + bool combine(const ITimingCache& inputCache, bool ignoreMismatch) noexcept + { + return mImpl->combine(inputCache, ignoreMismatch); + } + + //! + //! \brief Empty the timing cache + //! + //! \return True if reset successfully, false otherwise. + //! + bool reset() noexcept + { + return mImpl->reset(); + } + +protected: + apiv::VTimingCache* mImpl; }; -//! Maximum number of tactic sources in TacticSource enum. \see TacticSource -template <> -constexpr inline int32_t EnumMax() -{ - return 2; -} - -//! -//! \brief Represents a collection of one or more TacticSource values -//! combine using bitwise-OR operations. -//! -//! \see IBuilderConfig::setTacticSources(), IBuilderConfig::getTacticSources() -//! -using TacticSources = uint32_t; - //! //! \class IBuilderConfig //! //! \brief Holds properties for configuring a builder to produce an engine. \see BuilderFlags //! -class IBuilderConfig +class IBuilderConfig : public INoCopy { public: + virtual ~IBuilderConfig() noexcept = default; + //! //! \brief Set the number of minimization iterations used when timing layers. //! @@ -6483,7 +7205,10 @@ public: //! //! \see getMinTimingIterations() //! - virtual void setMinTimingIterations(int32_t minTiming) TRTNOEXCEPT = 0; + virtual void setMinTimingIterations(int32_t minTiming) noexcept + { + mImpl->setMinTimingIterations(minTiming); + } //! //! \brief Query the number of minimization iterations. @@ -6492,7 +7217,10 @@ public: //! //! \see setMinTimingIterations() //! - virtual int32_t getMinTimingIterations() const TRTNOEXCEPT = 0; + virtual int32_t getMinTimingIterations() const noexcept + { + return mImpl->getMinTimingIterations(); + } //! //! \brief Set the number of averaging iterations used when timing layers. @@ -6502,7 +7230,10 @@ public: //! //! \see getAvgTimingIterations() //! - virtual void setAvgTimingIterations(int32_t avgTiming) TRTNOEXCEPT = 0; + virtual void setAvgTimingIterations(int32_t avgTiming) noexcept + { + mImpl->setAvgTimingIterations(avgTiming); + } //! //! \brief Query the number of averaging iterations. @@ -6511,7 +7242,10 @@ public: //! //! \see setAvgTimingIterations() //! - virtual int32_t getAvgTimingIterations() const TRTNOEXCEPT = 0; + int32_t getAvgTimingIterations() const noexcept + { + return mImpl->getAvgTimingIterations(); + } //! //! \brief Configure the builder to target specified EngineCapability flow. @@ -6521,28 +7255,40 @@ public: //! //! The supported flows are specified in the EngineCapability enum. //! - virtual void setEngineCapability(EngineCapability capability) TRTNOEXCEPT = 0; + void setEngineCapability(EngineCapability capability) noexcept + { + mImpl->setEngineCapability(capability); + } //! //! \brief Query EngineCapability flow configured for the builder. //! - //! By default it returns EngineCapability::kDEFAULT. + //! By default it returns EngineCapability::kSTANDARD. //! //! \see setEngineCapability() //! - virtual EngineCapability getEngineCapability() const TRTNOEXCEPT = 0; + EngineCapability getEngineCapability() const noexcept + { + return mImpl->getEngineCapability(); + } //! //! \brief Set Int8 Calibration interface. //! //! The calibrator is to minimize the information loss during the INT8 quantization process. //! - virtual void setInt8Calibrator(IInt8Calibrator* calibrator) TRTNOEXCEPT = 0; + void setInt8Calibrator(IInt8Calibrator* calibrator) noexcept + { + mImpl->setInt8Calibrator(calibrator); + } //! //! \brief Get Int8 Calibration interface. //! - virtual IInt8Calibrator* getInt8Calibrator() const TRTNOEXCEPT = 0; + IInt8Calibrator* getInt8Calibrator() const noexcept + { + return mImpl->getInt8Calibrator(); + } //! //! \brief Set the maximum workspace size. @@ -6551,7 +7297,10 @@ public: //! //! \see getMaxWorkspaceSize() //! - virtual void setMaxWorkspaceSize(std::size_t workspaceSize) TRTNOEXCEPT = 0; + void setMaxWorkspaceSize(std::size_t workspaceSize) noexcept + { + mImpl->setMaxWorkspaceSize(workspaceSize); + } //! //! \brief Get the maximum workspace size. @@ -6562,7 +7311,10 @@ public: //! //! \see setMaxWorkspaceSize() //! - virtual std::size_t getMaxWorkspaceSize() const TRTNOEXCEPT = 0; + std::size_t getMaxWorkspaceSize() const noexcept + { + return mImpl->getMaxWorkspaceSize(); + } //! //! \brief Set the build mode flags to turn on builder options for this network. @@ -6576,7 +7328,10 @@ public: //! //! \see getFlags() //! - virtual void setFlags(BuilderFlags builderFlags) TRTNOEXCEPT = 0; + void setFlags(BuilderFlags builderFlags) noexcept + { + mImpl->setFlags(builderFlags); + } //! //! \brief Get the build mode flags for this builder config. Defaults to 0. @@ -6585,7 +7340,10 @@ public: //! //! \see setFlags() //! - virtual BuilderFlags getFlags() const TRTNOEXCEPT = 0; + BuilderFlags getFlags() const noexcept + { + return mImpl->getFlags(); + } //! //! \brief clear a single build mode flag. @@ -6594,7 +7352,10 @@ public: //! //! \see setFlags() //! - virtual void clearFlag(BuilderFlag builderFlag) TRTNOEXCEPT = 0; + void clearFlag(BuilderFlag builderFlag) noexcept + { + mImpl->clearFlag(builderFlag); + } //! //! \brief Set a single build mode flag. @@ -6603,7 +7364,10 @@ public: //! //! \see setFlags() //! - virtual void setFlag(BuilderFlag builderFlag) TRTNOEXCEPT = 0; + void setFlag(BuilderFlag builderFlag) noexcept + { + mImpl->setFlag(builderFlag); + } //! //! \brief Returns true if the build mode flag is set @@ -6612,7 +7376,10 @@ public: //! //! \return True if flag is set, false if unset. //! - virtual bool getFlag(BuilderFlag builderFlag) const TRTNOEXCEPT = 0; + bool getFlag(BuilderFlag builderFlag) const noexcept + { + return mImpl->getFlag(builderFlag); + } //! //! \brief Set the device that this layer must execute on. @@ -6620,37 +7387,52 @@ public: //! If DeviceType is not set or is reset, TensorRT will use the default DeviceType set in the builder. //! //! \note The device type for a layer must be compatible with the safety flow (if specified). - //! For example a layer cannot be marked for DLA execution while the builder is configured for kSAFE_GPU. + //! For example a layer cannot be marked for DLA execution while the builder is configured for kSAFETY. //! //! \see getDeviceType() //! - virtual void setDeviceType(const ILayer* layer, DeviceType deviceType) TRTNOEXCEPT = 0; + void setDeviceType(const ILayer* layer, DeviceType deviceType) noexcept + { + mImpl->setDeviceType(layer, deviceType); + } //! //! \brief Get the device that this layer executes on. //! \return Returns DeviceType of the layer. //! - virtual DeviceType getDeviceType(const ILayer* layer) const TRTNOEXCEPT = 0; + DeviceType getDeviceType(const ILayer* layer) const noexcept + { + return mImpl->getDeviceType(layer); + } //! //! \brief whether the DeviceType has been explicitly set for this layer //! \return true if device type is not default //! \see setDeviceType() getDeviceType() resetDeviceType() //! - virtual bool isDeviceTypeSet(const ILayer* layer) const TRTNOEXCEPT = 0; + bool isDeviceTypeSet(const ILayer* layer) const noexcept + { + return mImpl->isDeviceTypeSet(layer); + } //! //! \brief reset the DeviceType for this layer //! //! \see setDeviceType() getDeviceType() isDeviceTypeSet() //! - virtual void resetDeviceType(const ILayer* layer) TRTNOEXCEPT = 0; + void resetDeviceType(const ILayer* layer) noexcept + { + mImpl->resetDeviceType(layer); + } //! //! \brief Checks if a layer can run on DLA. //! \return status true if the layer can on DLA else returns false. //! - virtual bool canRunOnDLA(const ILayer* layer) const TRTNOEXCEPT = 0; + bool canRunOnDLA(const ILayer* layer) const noexcept + { + return mImpl->canRunOnDLA(layer); + } //! //! \brief Sets the DLA core used by the network. @@ -6662,7 +7444,10 @@ public: //! //! \warning Starting with TensorRT 8, the default value will be -1 if the DLA is not specified or unused. //! - virtual void setDLACore(int32_t dlaCore) TRTNOEXCEPT = 0; + void setDLACore(int32_t dlaCore) noexcept + { + mImpl->setDLACore(dlaCore); + } //! //! \brief Get the DLA core that the engine executes on. @@ -6670,53 +7455,78 @@ public: //! //! \warning Starting with TensorRT 8, the default value will be -1 if the DLA is not specified or unused. //! - virtual int32_t getDLACore() const TRTNOEXCEPT = 0; + int32_t getDLACore() const noexcept + { + return mImpl->getDLACore(); + } //! //! \brief Sets the default DeviceType to be used by the builder. It ensures that all the layers that can run on //! this device will run on it, unless setDeviceType is used to override the default DeviceType for a layer. //! \see getDefaultDeviceType() //! - virtual void setDefaultDeviceType(DeviceType deviceType) TRTNOEXCEPT = 0; + void setDefaultDeviceType(DeviceType deviceType) noexcept + { + mImpl->setDefaultDeviceType(deviceType); + } //! //! \brief Get the default DeviceType which was set by setDefaultDeviceType. //! //! By default it returns DeviceType::kGPU. //! - virtual DeviceType getDefaultDeviceType() const TRTNOEXCEPT = 0; + DeviceType getDefaultDeviceType() const noexcept + { + return mImpl->getDefaultDeviceType(); + } //! //! \brief Resets the builder configuration to defaults. //! //! When initializing a builder config object, we can call this function. //! - virtual void reset() TRTNOEXCEPT = 0; + void reset() noexcept + { + mImpl->reset(); + } //! //! \brief De-allocates any internally allocated memory. //! //! When destroying a builder config object, we can call this function. //! - virtual void destroy() TRTNOEXCEPT = 0; + //! \deprecated Deprecated interface will be removed in TensorRT 10.0. + //! + //! \warning Calling destroy on a managed pointer will result in a double-free error. + //! + TRT_DEPRECATED void destroy() noexcept + { + delete this; + } //! - //! \brief Set the cudaStream that is used to profile this network. + //! \brief Set the cuda stream that is used to profile this network. //! //! \param stream The cuda stream used for profiling by the builder. //! //! \see getProfileStream() //! - virtual void setProfileStream(const cudaStream_t stream) TRTNOEXCEPT = 0; + void setProfileStream(const cudaStream_t stream) noexcept + { + return mImpl->setProfileStream(stream); + } //! - //! \brief Get the cudaStream that is used to profile this network. + //! \brief Get the cuda stream that is used to profile this network. //! - //! \return The cuda stream used for profiling by the builder. + //! \return The cuda stream set by setProfileStream, nullptr if setProfileStream has not been called. //! //! \see setProfileStream() //! - virtual cudaStream_t getProfileStream() const TRTNOEXCEPT = 0; + cudaStream_t getProfileStream() const noexcept + { + return mImpl->getProfileStream(); + } //! //! \brief Add an optimization profile. @@ -6729,7 +7539,10 @@ public: //! \return The index of the optimization profile (starting from 0) if the input is valid, or -1 if the input is //! not valid. //! - virtual int32_t addOptimizationProfile(const IOptimizationProfile* profile) noexcept = 0; + int32_t addOptimizationProfile(const IOptimizationProfile* profile) noexcept + { + return mImpl->addOptimizationProfile(profile); + } //! //! \brief Get number of optimization profiles. @@ -6739,12 +7552,11 @@ public: //! //! \return The number of the optimization profiles. //! - virtual int32_t getNbOptimizationProfiles() const noexcept = 0; + int32_t getNbOptimizationProfiles() const noexcept + { + return mImpl->getNbOptimizationProfiles(); + } -protected: - virtual ~IBuilderConfig() {} - -public: //! //! \brief Set verbosity level of layer information exposed in NVTX annotations. //! @@ -6752,7 +7564,10 @@ public: //! //! \see ProfilingVerbosity, getProfilingVerbosity() //! - virtual void setProfilingVerbosity(ProfilingVerbosity verbosity) TRTNOEXCEPT = 0; + void setProfilingVerbosity(ProfilingVerbosity verbosity) noexcept + { + mImpl->setProfilingVerbosity(verbosity); + } //! //! \brief Get verbosity level of layer information exposed in NVTX annotations. @@ -6762,36 +7577,52 @@ public: //! //! \see ProfilingVerbosity, setProfilingVerbosity() //! - virtual ProfilingVerbosity getProfilingVerbosity() const TRTNOEXCEPT = 0; + ProfilingVerbosity getProfilingVerbosity() const noexcept + { + return mImpl->getProfilingVerbosity(); + } //! //! \brief Set Algorithm Selector. //! //! \param selector The algorithm selector to be set in the build config. - virtual void setAlgorithmSelector(IAlgorithmSelector* selector) TRTNOEXCEPT = 0; + void setAlgorithmSelector(IAlgorithmSelector* selector) noexcept + { + mImpl->setAlgorithmSelector(selector); + } //! //! \brief Get Algorithm Selector. //! - virtual IAlgorithmSelector* getAlgorithmSelector() const TRTNOEXCEPT = 0; + IAlgorithmSelector* getAlgorithmSelector() const noexcept + { + return mImpl->getAlgorithmSelector(); + } //! //! \brief Add a calibration profile. //! - //! Calibration optimization profile must be set if int8 calibration is used to set scales for a network with runtime dimensions. + //! Calibration optimization profile must be set if int8 calibration is used to set scales for a network with + //! runtime dimensions. //! //! \param profile The new calibration profile, which must satisfy profile->isValid() == true or be nullptr. //! MIN and MAX values will be overwritten by kOPT. //! \return True if the calibration profile was set correctly. //! - virtual bool setCalibrationProfile(const IOptimizationProfile* profile) noexcept = 0; + bool setCalibrationProfile(const IOptimizationProfile* profile) noexcept + { + return mImpl->setCalibrationProfile(profile); + } //! //! \brief Get the current calibration profile. //! //! \return A pointer to the current calibration profile or nullptr if calibration profile is unset. //! - virtual const IOptimizationProfile* getCalibrationProfile() noexcept = 0; + const IOptimizationProfile* getCalibrationProfile() noexcept + { + return mImpl->getCalibrationProfile(); + } //! //! \brief Set the quantization flags. @@ -6805,7 +7636,10 @@ public: //! //! \see getQuantizationFlags() //! - virtual void setQuantizationFlags(QuantizationFlags flags) TRTNOEXCEPT = 0; + void setQuantizationFlags(QuantizationFlags flags) noexcept + { + mImpl->setQuantizationFlags(flags); + } //! //! \brief Get the quantization flags. @@ -6814,7 +7648,10 @@ public: //! //! \see setQuantizationFlag() //! - virtual QuantizationFlags getQuantizationFlags() const TRTNOEXCEPT = 0; + QuantizationFlags getQuantizationFlags() const noexcept + { + return mImpl->getQuantizationFlags(); + } //! //! \brief clear a quantization flag. @@ -6823,7 +7660,10 @@ public: //! //! \see setQuantizationFlags() //! - virtual void clearQuantizationFlag(QuantizationFlag flag) TRTNOEXCEPT = 0; + void clearQuantizationFlag(QuantizationFlag flag) noexcept + { + mImpl->clearQuantizationFlag(flag); + } //! //! \brief Set a single quantization flag. @@ -6832,7 +7672,10 @@ public: //! //! \see setQuantizationFlags() //! - virtual void setQuantizationFlag(QuantizationFlag flag) TRTNOEXCEPT = 0; + void setQuantizationFlag(QuantizationFlag flag) noexcept + { + mImpl->setQuantizationFlag(flag); + } //! //! \brief Returns true if the quantization flag is set. @@ -6841,7 +7684,10 @@ public: //! //! \return True if quantization flag is set, false if unset. //! - virtual bool getQuantizationFlag(QuantizationFlag flag) const TRTNOEXCEPT = 0; + bool getQuantizationFlag(QuantizationFlag flag) const noexcept + { + return mImpl->getQuantizationFlag(flag); + } //! //! \brief Set tactic sources. @@ -6849,8 +7695,8 @@ public: //! This bitset controls which tactic sources TensorRT is allowed to use for tactic //! selection. //! - //! By default, kCUBLAS is always enabled. kCUBLAS_LT is enabled for x86 - //! platforms, as well as non-x86 platforms if CUDA >= 11.0 + //! By default, kCUBLAS and kCUDNN are always enabled. kCUBLAS_LT is enabled for x86 + //! platforms as well as non-x86 platforms when CUDA >= 11.0. //! //! Multiple tactic sources may be combined with a bitwise OR operation. For example, //! to enable cublas and cublasLt as tactic sources, use a value of: @@ -6863,7 +7709,10 @@ public: //! \return true if the tactic sources in the build configuration were updated. //! The tactic sources in the build configuration will not be updated if the provided value is invalid. //! - virtual bool setTacticSources(TacticSources tacticSources) TRTNOEXCEPT = 0; + bool setTacticSources(TacticSources tacticSources) noexcept + { + return mImpl->setTacticSources(tacticSources); + } //! //! \brief Get tactic sources. @@ -6875,18 +7724,74 @@ public: //! //! \return tactic sources //! - virtual TacticSources getTacticSources() const TRTNOEXCEPT = 0; + TacticSources getTacticSources() const noexcept + { + return mImpl->getTacticSources(); + } + + //! + //! \brief Create timing cache + //! + //! Create ITimingCache instance from serialized raw data. The created timing cache doesn’t belong to + //! a specific IBuilderConfig. It can be shared by multiple builder instances. Call setTimingCache() + //! before launching a builder to attach cache to builder instance. + //! + //! \param blob A pointer to the raw data that contains serialized timing cache + //! \param size The size in bytes of the serialized timing cache. Size 0 means create a new cache from scratch + //! + //! \see setTimingCache + //! + //! \return the pointer to ITimingCache created + //! + nvinfer1::ITimingCache* createTimingCache(const void* blob, std::size_t size) const noexcept + { + return mImpl->createTimingCache(blob, size); + } + + //! + //! \brief Attach a timing cache to IBuilderConfig + //! + //! The timing cache has verification header to make sure the provided cache can be used in current environment. + //! A failure will be reported if the CUDA device property in the provided cache is different from current + //! environment. ignoreMismatch = true skips strict verification and allows loading cache created from a different + //! device. + //! + //! The cache must not be destroyed until after the engine is built. + //! + //! \param cache the timing cache to be used + //! \param ignoreMismatch whether or not allow using a cache that contains different CUDA device property + //! + //! \return true if set successfully, false otherwise + //! + //! \warning Using cache generated from devices with different CUDA device properties may lead to + //! functional/performance bugs. + //! + bool setTimingCache(const ITimingCache& cache, bool ignoreMismatch) noexcept + { + return mImpl->setTimingCache(cache, ignoreMismatch); + } + + //! + //! \brief Get the pointer to the timing cache from current IBuilderConfig + //! + //! \return pointer to the timing cache used in current IBuilderConfig + //! + const nvinfer1::ITimingCache* getTimingCache() const noexcept + { + return mImpl->getTimingCache(); + } + +protected: + apiv::VBuilderConfig* mImpl; }; -//! \typedef NetworkDefinitionCreationFlags -//! -//! \brief This bitset is capable of representing one or more NetworkDefinitionCreationFlag flags -//! constructed with binary OR operations. +//! \brief Represents one or more NetworkDefinitionCreationFlag flags +//! using binary OR operations. //! e.g., 1U << NetworkDefinitionCreationFlag::kEXPLICIT_BATCH //! //! \see IBuilder::createNetworkV2 //! -typedef uint32_t NetworkDefinitionCreationFlags; +using NetworkDefinitionCreationFlags = uint32_t; //! \enum NetworkDefinitionCreationFlag //! @@ -6917,12 +7822,12 @@ enum class NetworkDefinitionCreationFlag : int32_t //! precision network is [-127,127]. //! 5) Quantizing and dequantizing activation values between higher (FP32) and lower (INT8) precision //! will be performed using explicit Scale layers with input/output precision set appropriately. - kEXPLICIT_PRECISION = 1, //!< Mark the network to be an explicit precision network + kEXPLICIT_PRECISION TRT_DEPRECATED_ENUM = 1, //! <-- Deprecated, used for backward compatibility }; //! Maximum number of elements in NetworkDefinitionCreationFlag enum. \see NetworkDefinitionCreationFlag template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 2; } @@ -6934,21 +7839,10 @@ constexpr inline int32_t EnumMax() //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. //! -class IBuilder +class IBuilder : public INoCopy { public: - //! - //! \brief Create a network definition object where all tensors have an implicit batch dimension. - //! - //! This method is equivalent to createNetworkV2(0U), and retained for - //! compatibility - //! with earlier version of TensorRT. The network does not support dynamic shapes or explicit batch sizes. - //! - //! \see INetworkDefinition, createNetworkV2 - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilder::createNetworkV2 instead. - //! - TRT_DEPRECATED virtual nvinfer1::INetworkDefinition* createNetwork() TRTNOEXCEPT = 0; + virtual ~IBuilder() noexcept = default; //! //! \brief Set the maximum batch size. @@ -6958,7 +7852,10 @@ public: //! //! \see getMaxBatchSize() //! - virtual void setMaxBatchSize(int32_t batchSize) TRTNOEXCEPT = 0; + void setMaxBatchSize(int32_t batchSize) noexcept + { + mImpl->setMaxBatchSize(batchSize); + } //! //! \brief Get the maximum batch size. @@ -6968,231 +7865,38 @@ public: //! \see setMaxBatchSize() //! \see getMaxDLABatchSize() //! - virtual int32_t getMaxBatchSize() const TRTNOEXCEPT = 0; - - //! - //! \brief Set the maximum workspace size. - //! - //! \param workspaceSize The maximum GPU temporary memory which the engine can use at execution time. - //! - //! \see getMaxWorkspaceSize() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::setMaxWorkspaceSize instead. - //! - TRT_DEPRECATED virtual void setMaxWorkspaceSize(std::size_t workspaceSize) TRTNOEXCEPT = 0; - - //! - //! \brief Get the maximum workspace size. - //! - //! \return The maximum workspace size. - //! - //! \see setMaxWorkspaceSize() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::getMaxWorkspaceSize instead. - //! - TRT_DEPRECATED virtual std::size_t getMaxWorkspaceSize() const TRTNOEXCEPT = 0; - - //! - //! \brief Set whether half2 mode is used. - //! - //! half2 mode is a paired-image mode that is significantly faster for batch sizes greater than one on platforms - //! with fp16 support. - //! - //! \param mode Whether half2 mode is used. - //! - //! \see getHalf2Mode() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::setFlag instead. - //! - TRT_DEPRECATED virtual void setHalf2Mode(bool mode) TRTNOEXCEPT = 0; - - //! - //! \brief Query whether half2 mode is used. - //! - //! \see setHalf2Mode() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::getFlag instead. - //! - TRT_DEPRECATED virtual bool getHalf2Mode() const TRTNOEXCEPT = 0; - - //! - //! \brief Set whether the builder should use debug synchronization. - //! - //! If this flag is true, the builder will synchronize after timing each layer, and report the layer name. It can - //! be useful when diagnosing issues at build time. - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::setFlag instead. - //! - TRT_DEPRECATED virtual void setDebugSync(bool sync) TRTNOEXCEPT = 0; - - //! - //! \brief Query whether the builder will use debug synchronization. - //! - //! \see setDebugSync() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::getFlag instead. - //! - TRT_DEPRECATED virtual bool getDebugSync() const TRTNOEXCEPT = 0; - - //! - //! \brief Set the number of minimization iterations used when timing layers. - //! - //! When timing layers, the builder minimizes over a set of average times for layer execution. This parameter - //! controls the number of iterations used in minimization. - //! - //! \see getMinFindIterations() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::setMinTimingIterations instead. - //! - TRT_DEPRECATED virtual void setMinFindIterations(int32_t minFind) TRTNOEXCEPT = 0; - - //! - //! \brief Query the number of minimization iterations. - //! - //! \see setMinFindIterations() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::getMinTimingIterations instead. - //! - TRT_DEPRECATED virtual int32_t getMinFindIterations() const TRTNOEXCEPT = 0; - - //! - //! \brief Set the number of averaging iterations used when timing layers. - //! - //! When timing layers, the builder minimizes over a set of average times for layer execution. This parameter - //! controls the number of iterations used in averaging. - //! - //! \see getAverageFindIterations() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::setAvgTimingIterations instead. - //! - TRT_DEPRECATED virtual void setAverageFindIterations(int32_t avgFind) TRTNOEXCEPT = 0; - - //! - //! \brief Query the number of averaging iterations. - //! - //! \see setAverageFindIterations() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::getAvgTimingIterations instead. - //! - TRT_DEPRECATED virtual int32_t getAverageFindIterations() const TRTNOEXCEPT = 0; - - //! - //! \brief Build a CUDA engine from a network definition. - //! - //! \see INetworkDefinition ICudaEngine - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilder::buildEngineWithConfig instead. - //! - TRT_DEPRECATED virtual nvinfer1::ICudaEngine* buildCudaEngine( - nvinfer1::INetworkDefinition& network) TRTNOEXCEPT = 0; + int32_t getMaxBatchSize() const noexcept + { + return mImpl->getMaxBatchSize(); + } //! //! \brief Determine whether the platform has fast native fp16. //! - virtual bool platformHasFastFp16() const TRTNOEXCEPT = 0; + bool platformHasFastFp16() const noexcept + { + return mImpl->platformHasFastFp16(); + } //! //! \brief Determine whether the platform has fast native int8. //! - virtual bool platformHasFastInt8() const TRTNOEXCEPT = 0; + bool platformHasFastInt8() const noexcept + { + return mImpl->platformHasFastInt8(); + } //! //! \brief Destroy this object. //! - virtual void destroy() TRTNOEXCEPT = 0; - + //! \deprecated Deprecated interface will be removed in TensorRT 10.0. //! - //! \brief Set whether or not quantized 8-bit kernels are permitted. + //! \warning Calling destroy on a managed pointer will result in a double-free error. //! - //! During engine build int8 kernels will also be tried when this mode is enabled. - //! - //! \param mode Whether quantized 8-bit kernels are permitted. - //! - //! \see getInt8Mode() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::setFlag instead. - //! - TRT_DEPRECATED virtual void setInt8Mode(bool mode) TRTNOEXCEPT = 0; - - //! - //! \brief Query whether Int8 mode is used. - //! - //! \see setInt8Mode() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::getFlag instead. - //! - TRT_DEPRECATED virtual bool getInt8Mode() const TRTNOEXCEPT = 0; - - //! - //! \brief Set Int8 Calibration interface. - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::setInt8Calibrator instead. - //! - TRT_DEPRECATED virtual void setInt8Calibrator(IInt8Calibrator* calibrator) TRTNOEXCEPT = 0; - - //! - //! \brief Set the device that this layer must execute on. - //! \param DeviceType that this layer must execute on. - //! If DeviceType is not set or is reset, TensorRT will use the default DeviceType set in the builder. - //! - //! \note The device type for a layer must be compatible with the safety flow (if specified). - //! For example a layer cannot be marked for DLA execution while the builder is configured for kSAFE_GPU. - //! - //! \see getDeviceType() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::setDeviceType instead. - //! - TRT_DEPRECATED virtual void setDeviceType(ILayer* layer, DeviceType deviceType) TRTNOEXCEPT = 0; - - //! - //! \brief Get the device that this layer executes on. - //! \return Returns DeviceType of the layer. - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::getDeviceType instead. - //! - TRT_DEPRECATED virtual DeviceType getDeviceType(const ILayer* layer) const TRTNOEXCEPT = 0; - - //! - //! \brief whether the DeviceType has been explicitly set for this layer - //! \return whether the DeviceType has been explicitly set - //! \see setDeviceType() getDeviceType() resetDeviceType() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::isDeviceTypeSet instead. - //! - TRT_DEPRECATED virtual bool isDeviceTypeSet(const ILayer* layer) const TRTNOEXCEPT = 0; - - //! - //! \brief reset the DeviceType for this layer - //! - //! \see setDeviceType() getDeviceType() isDeviceTypeSet() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::resetDeviceType instead. - //! - TRT_DEPRECATED virtual void resetDeviceType(ILayer* layer) TRTNOEXCEPT = 0; - - //! - //! \brief Checks if a layer can run on DLA. - //! \return status true if the layer can on DLA else returns false. - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::canRunOnDLA instead. - //! - TRT_DEPRECATED virtual bool canRunOnDLA(const ILayer* layer) const TRTNOEXCEPT = 0; - - //! - //! \brief Sets the default DeviceType to be used by the builder. It ensures that all the layers that can run on - //! this device will run on it, unless setDeviceType is used to override the default DeviceType for a layer. - //! \see getDefaultDeviceType() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::setDefaultDeviceType instead. - //! - TRT_DEPRECATED virtual void setDefaultDeviceType(DeviceType deviceType) TRTNOEXCEPT = 0; - - //! - //! \brief Get the default DeviceType which was set by setDefaultDeviceType. - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::getDefaultDeviceType instead. - //! - TRT_DEPRECATED virtual DeviceType getDefaultDeviceType() const TRTNOEXCEPT = 0; + TRT_DEPRECATED void destroy() noexcept + { + delete this; + } //! //! \brief Get the maximum batch size DLA can support. @@ -7201,57 +7905,19 @@ public: //! //! \warning getMaxDLABatchSize does not work with dynamic shapes. //! - virtual int32_t getMaxDLABatchSize() const TRTNOEXCEPT = 0; - - //! - //! \brief Sets the builder to use GPU if a layer that was supposed to run on DLA can not run on DLA. - //! \param Allows fallback if setFallBackMode is true else disables fallback option. - //! - //! \note GPU fallback may only be specified for non-safety modes. \see EngineCapability - //! Simultaneously enabling GPU fallback and safety-restricted modes is disallowed. - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::setFlag instead. - //! - TRT_DEPRECATED virtual void allowGPUFallback(bool setFallBackMode) TRTNOEXCEPT = 0; + int32_t getMaxDLABatchSize() const noexcept + { + return mImpl->getMaxDLABatchSize(); + } //! //! \brief Return the number of DLA engines available to this builder. //! - virtual int32_t getNbDLACores() const TRTNOEXCEPT = 0; - - //! - //! \brief Set the DLA core that the engine must execute on. - //! \param dlaCore The DLA core to execute the engine on (0 to N-1, where N is the maximum number of DLA cores - //! present on the device). Default value is 0. - //! DLA Core is not a property of the engine that is preserved by serialization: when the engine is deserialized - //! it will be associated with the DLA core which is configured for the runtime. - //! \see IRuntime::setDLACore() getDLACore() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::setDLACore instead. - //! - TRT_DEPRECATED virtual void setDLACore(int32_t dlaCore) TRTNOEXCEPT = 0; - - //! - //! \brief Get the DLA core that the engine executes on. - //! \return If setDLACore is called, returns DLA core from 0 to N-1, else returns 0. - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::getDLACore instead. - //! - TRT_DEPRECATED virtual int32_t getDLACore() const TRTNOEXCEPT = 0; - - //! - //! \brief Resets the builder state - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilder::reset instead. - //! - TRT_DEPRECATED virtual void reset(nvinfer1::INetworkDefinition& network) TRTNOEXCEPT = 0; - -protected: - virtual ~IBuilder() + int32_t getNbDLACores() const noexcept { + return mImpl->getNbDLACores(); } -public: //! //! \brief Set the GPU allocator. //! \param allocator Set the GPU allocator to be used by the builder. All GPU memory acquired will use this @@ -7263,97 +7929,20 @@ public: //! must span the lifetime of those engines as //! well as that of the builder. If nullptr is passed, the default allocator will be used. //! - virtual void setGpuAllocator(IGpuAllocator* allocator) TRTNOEXCEPT = 0; - - //! - //! \brief Set whether or not 16-bit kernels are permitted. - //! - //! During engine build fp16 kernels will also be tried when this mode is enabled. - //! - //! \param mode Whether 16-bit kernels are permitted. - //! - //! \see getFp16Mode() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::setFlag instead. - //! - TRT_DEPRECATED virtual void setFp16Mode(bool mode) TRTNOEXCEPT = 0; - - //! - //! \brief Query whether 16-bit kernels are permitted. - //! - //! \see setFp16Mode() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::getFlag instead. - //! - TRT_DEPRECATED virtual bool getFp16Mode() const TRTNOEXCEPT = 0; - - //! - //! \brief Set whether or not type constraints are strict. - //! - //! When strict type constraints are in use, TensorRT will always choose a layer implementation that conforms to the - //! type constraints specified, if one exists. If this flag is not set, a higher-precision implementation may be - //! chosen if it results in higher performance. - //! - //! If no conformant layer exists, TensorRT will choose a non-conformant layer if available regardless of the - //! setting of this flag. - //! - //! See the developer guide for the definition of strictness. - //! - //! \param mode Whether type constraints are strict - //! - //! \see getStrictTypeConstraints() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::setFlag instead. - //! - TRT_DEPRECATED virtual void setStrictTypeConstraints(bool mode) TRTNOEXCEPT = 0; - - //! - //! \brief Query whether or not type constraints are strict. - //! - //! \see setStrictTypeConstraints() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::getFlag instead. - //! - TRT_DEPRECATED virtual bool getStrictTypeConstraints() const TRTNOEXCEPT = 0; - - //! - //! Set whether engines will be refittable. - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::setFlag instead. - //! - TRT_DEPRECATED virtual void setRefittable(bool canRefit) TRTNOEXCEPT = 0; - - //! - //! \brief Query whether or not engines will be refittable. - //! - //! \see getRefittable() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::getFlag instead. - //! - TRT_DEPRECATED virtual bool getRefittable() const TRTNOEXCEPT = 0; - - //! - //! \brief Configure the builder to target specified EngineCapability flow. - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::setEngineCapability instead. - //! - TRT_DEPRECATED virtual void setEngineCapability(EngineCapability capability) TRTNOEXCEPT = 0; - - //! - //! \brief Query EngineCapability flow configured for the builder. - //! - //! \see setEngineCapability() - //! - //! \deprecated API will be removed in TensorRT 8.0, use IBuilderConfig::getEngineCapability instead. - //! - TRT_DEPRECATED virtual EngineCapability getEngineCapability() const TRTNOEXCEPT = 0; + void setGpuAllocator(IGpuAllocator* allocator) noexcept + { + mImpl->setGpuAllocator(allocator); + } //! //! \brief Create a builder configuration object. //! //! \see IBuilderConfig //! - virtual nvinfer1::IBuilderConfig* createBuilderConfig() TRTNOEXCEPT = 0; + nvinfer1::IBuilderConfig* createBuilderConfig() noexcept + { + return mImpl->createBuilderConfig(); + } //! //! \brief Builds an engine for the given INetworkDefinition and given IBuilderConfig. @@ -7361,9 +7950,15 @@ public: //! It enables the builder to build multiple engines based on the same network definition, but with different //! builder configurations. //! - virtual nvinfer1::ICudaEngine* buildEngineWithConfig( - INetworkDefinition& network, IBuilderConfig& config) TRTNOEXCEPT = 0; - + //! \note This function will synchronize the cuda stream returned by \p config.getProfileStream() before returning. + //! + //! \deprecated API will be removed in TensorRT 10.0, use IBuilder::buildSerializedNetwork instead. + //! + TRT_DEPRECATED nvinfer1::ICudaEngine* buildEngineWithConfig( + INetworkDefinition& network, IBuilderConfig& config) noexcept + { + return mImpl->buildEngineWithConfig(network, config); + } //! \brief Create a network definition object //! @@ -7376,8 +7971,10 @@ public: //! //! \see INetworkDefinition, NetworkDefinitionCreationFlags //! - virtual nvinfer1::INetworkDefinition* createNetworkV2(NetworkDefinitionCreationFlags flags) TRTNOEXCEPT = 0; - + nvinfer1::INetworkDefinition* createNetworkV2(NetworkDefinitionCreationFlags flags) noexcept + { + return mImpl->createNetworkV2(flags); + } //! \brief Create a new optimization profile. //! @@ -7388,7 +7985,10 @@ public: //! //! \see IOptimizationProfile //! - virtual nvinfer1::IOptimizationProfile* createOptimizationProfile() noexcept = 0; + nvinfer1::IOptimizationProfile* createOptimizationProfile() noexcept + { + return mImpl->createOptimizationProfile(); + } //! //! \brief Set the ErrorRecorder for this interface @@ -7398,33 +7998,91 @@ public: //! recorder to nullptr unregisters the recorder with the interface, resulting in a call to decRefCount if //! a recorder has been registered. //! + //! If an error recorder is not set, messages will be sent to the global log stream. + //! //! \param recorder The error recorder to register with this interface. // - //! \see getErrorRecorder + //! \see getErrorRecorder() //! - virtual void setErrorRecorder(IErrorRecorder* recorder) TRTNOEXCEPT = 0; + void setErrorRecorder(IErrorRecorder* recorder) noexcept + { + mImpl->setErrorRecorder(recorder); + } //! //! \brief get the ErrorRecorder assigned to this interface. //! - //! Retrieves the assigned error recorder object for the given class. A default error recorder does not exist, - //! so a nullptr will be returned if setErrorRecorder has not been called. + //! Retrieves the assigned error recorder object for the given class. + //! A nullptr will be returned if setErrorRecorder has not been called. //! //! \return A pointer to the IErrorRecorder object that has been registered. //! - //! \see setErrorRecorder + //! \see setErrorRecorder() //! - virtual IErrorRecorder* getErrorRecorder() const TRTNOEXCEPT = 0; + IErrorRecorder* getErrorRecorder() const noexcept + { + return mImpl->getErrorRecorder(); + } //! //! \brief Resets the builder state to default values. //! - virtual void reset() TRTNOEXCEPT = 0; + void reset() noexcept + { + mImpl->reset(); + } //! //! \brief Determine whether the platform has TF32 support. //! - virtual bool platformHasTf32() const TRTNOEXCEPT = 0; + bool platformHasTf32() const noexcept + { + return mImpl->platformHasTf32(); + } + + //! + //! \brief Builds and serializes a network for the given INetworkDefinition and IBuilderConfig. + //! + //! This function allows building and serialization of a network without creating an engine. + //! + //! \param network Network definition. + //! \param config Builder configuration. + //! + //! \return A pointer to a IHostMemory object that contains a serialized network. + //! + //! \note This function will synchronize the cuda stream returned by \p config.getProfileStream() before returning. + //! + //! \see INetworkDefinition, IBuilderConfig, IHostMemory + //! + nvinfer1::IHostMemory* buildSerializedNetwork(INetworkDefinition& network, IBuilderConfig& config) noexcept + { + return mImpl->buildSerializedNetwork(network, config); + } + + //! + //! \brief Checks that a network is within the scope of the IBuilderConfig settings. + //! + //! \param network The network definition to check for configuration compliance. + //! \param config The configuration of the builder to use when checking \p network. + //! + //! Given an INetworkDefinition, \p network, and an IBuilderConfig, \p config, check if + //! the network falls within the constraints of the builder configuration based on the + //! EngineCapability, BuilderFlag, and DeviceType. If the network is within the constraints, + //! then the function returns true, and false if a violation occurs. This function reports + //! the conditions that are violated to the registered ErrorRecorder. + //! + //! \return True if network is within the scope of the restrictions specified by the builder config, + //! false otherwise. + //! + //! \note This function will synchronize the cuda stream returned by \p config.getProfileStream() before returning. + //! + bool isNetworkSupported(INetworkDefinition const& network, IBuilderConfig const& config) const noexcept + { + return mImpl->isNetworkSupported(network, config); + } + +protected: + apiv::VBuilder* mImpl; }; } // namespace nvinfer1 @@ -7433,24 +8091,26 @@ public: //! Internal C entry point for creating IBuilder. //! @private //! -extern "C" TENSORRTAPI void* createInferBuilder_INTERNAL(void* logger, int32_t version); +extern "C" TENSORRTAPI void* createInferBuilder_INTERNAL(void* logger, int32_t version) noexcept; namespace nvinfer1 { namespace { + //! //! \brief Create an instance of an IBuilder class. //! -//! This class is the logging class for the builder. +//! This is the logging class for the builder. //! //! unnamed namespace avoids linkage surprises when linking objects built with different versions of this header. //! -inline IBuilder* createInferBuilder(ILogger& logger) +inline IBuilder* createInferBuilder(ILogger& logger) noexcept { return static_cast(createInferBuilder_INTERNAL(&logger, NV_TENSORRT_VERSION)); } -} -} -#endif +} // namespace +} // namespace nvinfer1 + +#endif // NV_INFER_H diff --git a/include/NvInferImpl.h b/include/NvInferImpl.h new file mode 100644 index 00000000..2b283e45 --- /dev/null +++ b/include/NvInferImpl.h @@ -0,0 +1,939 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ + +#ifndef NV_INFER_IMPL_H +#define NV_INFER_IMPL_H + +#include "NvInferLegacyDims.h" +#include "NvInferRuntimeCommon.h" + +namespace nvinfer1 +{ + +class IActivationLayer; +class IAlgorithm; +class IAlgorithmContext; +class IAlgorithmIOInfo; +class IAlgorithmSelector; +class IAlgorithmVariant; +class IBuilderConfig; +class IConcatenationLayer; +class IConstantLayer; +class IConvolutionLayer; +class ICudaEngine; +class IDeconvolutionLayer; +class IDequantizeLayer; +class IDimensionExpr; +class IElementWiseLayer; +class IExecutionContext; +class IFillLayer; +class IFullyConnectedLayer; +class IGatherLayer; +class IHostMemory; +class IIdentityLayer; +class IInt8Calibrator; +class IIteratorLayer; +class ILayer; +class ILoop; +class ILoopOutputLayer; +class ILRNLayer; +class IMatrixMultiplyLayer; +class INetworkDefinition; +class IOptimizationProfile; +class IPaddingLayer; +class IParametricReLULayer; +class IPlugin; +class IPluginExt; +class IPluginFactory; +class IPluginLayer; +class IPluginV2Layer; +class IPoolingLayer; +class IProfiler; +class IQuantizeLayer; +class IRaggedSoftMaxLayer; +class IRecurrenceLayer; +class IReduceLayer; +class IResizeLayer; +class IRNNv2Layer; +class IScaleLayer; +class ISelectLayer; +class IShapeLayer; +class IShuffleLayer; +class ISliceLayer; +class ISoftMaxLayer; +class ITensor; +class ITimingCache; +class ITopKLayer; +class ITripLimitLayer; +class IUnaryLayer; +struct Permutation; +class Weights; + +enum class ActivationType : int32_t; +enum class BuilderFlag : int32_t; +enum class CalibrationAlgoType : int32_t; +enum class DeviceType : int32_t; +enum class DimensionOperation : int32_t; +enum class ElementWiseOperation : int32_t; +enum class EngineCapability : int32_t; +enum class FillOperation : int32_t; +enum class LayerType : int32_t; +enum class LoopOutput : int32_t; +enum class MatrixOperation : int32_t; +enum class NetworkDefinitionCreationFlag : int32_t; +enum class OptProfileSelector : int32_t; +enum class PaddingMode : int32_t; +enum class PoolingType : int32_t; +enum class ProfilingVerbosity : int32_t; +enum class QuantizationFlag : int32_t; +enum class ReduceOperation : int32_t; +enum class ResizeCoordinateTransformation : int32_t; +enum class ResizeMode : int32_t; +enum class ResizeRoundMode : int32_t; +enum class ResizeSelector : int32_t; +enum class RNNDirection : int32_t; +enum class RNNGateType : int32_t; +enum class RNNInputMode : int32_t; +enum class RNNOperation : int32_t; +enum class ScaleMode : int32_t; +enum class SliceMode : int32_t; +enum class TensorLocation : int32_t; +enum class TopKOperation : int32_t; +enum class TripLimit : int32_t; +enum class UnaryOperation : int32_t; +enum class WeightsRole : int32_t; + +using TacticSources = uint32_t; +using TensorFormats = uint32_t; +using BuilderFlags = uint32_t; +using NetworkDefinitionCreationFlags = uint32_t; +using QuantizationFlags = uint32_t; + +//! +//! \file NvInferImpl.h +//! +//! This file contains definitions for API methods that cross the shared library boundary. These +//! methods must not be called directly by applications; they should only be called through the +//! API classes. +//! + +namespace apiv +{ + +class VRoot +{ +public: + virtual ~VRoot() noexcept = default; +}; + +class VHostMemory : public VRoot +{ +public: + virtual void* data() const noexcept = 0; + virtual std::size_t size() const noexcept = 0; + virtual DataType type() const noexcept = 0; +}; + +class VDimensionExpr : public VRoot +{ +public: + virtual bool isConstant() const = 0; + virtual int32_t getConstantValue() const = 0; +}; + +class VExprBuilder : public VRoot +{ +public: + virtual const IDimensionExpr* constant(int32_t value) = 0; + virtual const IDimensionExpr* operation( + DimensionOperation op, const IDimensionExpr& first, const IDimensionExpr& second) + = 0; +}; + +class VRuntime : public VRoot +{ +public: + virtual nvinfer1::ICudaEngine* deserializeCudaEngine( + const void* blob, std::size_t size, IPluginFactory* pluginFactory) noexcept + = 0; + virtual void setDLACore(int32_t dlaCore) noexcept = 0; + virtual int32_t getDLACore() const noexcept = 0; + virtual int32_t getNbDLACores() const noexcept = 0; + virtual void setGpuAllocator(IGpuAllocator* allocator) noexcept = 0; + virtual void setErrorRecorder(IErrorRecorder* recorder) noexcept = 0; + virtual IErrorRecorder* getErrorRecorder() const noexcept = 0; +}; + +class VRefitter : public VRoot +{ +public: + virtual bool setWeights(const char* layerName, WeightsRole role, const Weights weights) noexcept = 0; + virtual bool refitCudaEngine() noexcept = 0; + virtual int32_t getMissing(int32_t size, const char** layerNames, WeightsRole* roles) noexcept = 0; + virtual int32_t getAll(int32_t size, const char** layerNames, WeightsRole* roles) noexcept = 0; + virtual bool setDynamicRange(const char* tensorName, float min, float max) noexcept = 0; + virtual float getDynamicRangeMin(const char* tensorName) const noexcept = 0; + virtual float getDynamicRangeMax(const char* tensorName) const noexcept = 0; + virtual int32_t getTensorsWithDynamicRange(int32_t size, const char** tensorNames) const noexcept = 0; + virtual void setErrorRecorder(IErrorRecorder* recorder) noexcept = 0; + virtual IErrorRecorder* getErrorRecorder() const noexcept = 0; + virtual bool setNamedWeights(const char* name, Weights weights) noexcept = 0; + virtual int32_t getMissingWeights(int32_t size, const char** weightsNames) noexcept = 0; + virtual int32_t getAllWeights(int32_t size, const char** weightsNames) noexcept = 0; +}; + +class VOptimizationProfile : public VRoot +{ +public: + virtual bool setDimensions(const char* inputName, OptProfileSelector select, Dims dims) noexcept = 0; + virtual Dims getDimensions(const char* inputName, OptProfileSelector select) const noexcept = 0; + virtual bool setShapeValues( + const char* inputName, OptProfileSelector select, const int32_t* values, int32_t nbValues) noexcept + = 0; + virtual int32_t getNbShapeValues(const char* inputName) const noexcept = 0; + virtual int32_t const* getShapeValues(const char* inputName, OptProfileSelector select) const noexcept = 0; + virtual bool setExtraMemoryTarget(float target) noexcept = 0; + virtual float getExtraMemoryTarget() const noexcept = 0; + virtual bool isValid() const noexcept = 0; +}; + +class VCudaEngine : public VRoot +{ +public: + virtual int32_t getNbBindings() const noexcept = 0; + virtual int32_t getBindingIndex(const char* name) const noexcept = 0; + virtual const char* getBindingName(int32_t bindingIndex) const noexcept = 0; + virtual bool bindingIsInput(int32_t bindingIndex) const noexcept = 0; + virtual Dims getBindingDimensions(int32_t bindingIndex) const noexcept = 0; + virtual DataType getBindingDataType(int32_t bindingIndex) const noexcept = 0; + virtual int32_t getMaxBatchSize() const noexcept = 0; + virtual int32_t getNbLayers() const noexcept = 0; + virtual IHostMemory* serialize() const noexcept = 0; + virtual IExecutionContext* createExecutionContext() noexcept = 0; + virtual TensorLocation getLocation(int32_t bindingIndex) const noexcept = 0; + virtual IExecutionContext* createExecutionContextWithoutDeviceMemory() noexcept = 0; + virtual size_t getDeviceMemorySize() const noexcept = 0; + virtual bool isRefittable() const noexcept = 0; + virtual int32_t getBindingBytesPerComponent(int32_t bindingIndex) const noexcept = 0; + virtual int32_t getBindingComponentsPerElement(int32_t bindingIndex) const noexcept = 0; + virtual TensorFormat getBindingFormat(int32_t bindingIndex) const noexcept = 0; + virtual const char* getBindingFormatDesc(int32_t bindingIndex) const noexcept = 0; + virtual int32_t getBindingVectorizedDim(int32_t bindingIndex) const noexcept = 0; + virtual const char* getName() const noexcept = 0; + virtual int32_t getNbOptimizationProfiles() const noexcept = 0; + virtual Dims getProfileDimensions(int32_t bindingIndex, int32_t profileIndex, OptProfileSelector select) const + noexcept + = 0; + virtual const int32_t* getProfileShapeValues( + int32_t profileIndex, int32_t inputIndex, OptProfileSelector select) const noexcept + = 0; + virtual bool isShapeBinding(int32_t bindingIndex) const noexcept = 0; + virtual bool isExecutionBinding(int32_t bindingIndex) const noexcept = 0; + virtual EngineCapability getEngineCapability() const noexcept = 0; + virtual void setErrorRecorder(IErrorRecorder* recorder) noexcept = 0; + virtual IErrorRecorder* getErrorRecorder() const noexcept = 0; + virtual bool hasImplicitBatchDimension() const noexcept = 0; + virtual TacticSources getTacticSources() const noexcept = 0; +}; + +class VExecutionContext : public VRoot +{ +public: + virtual bool execute(int32_t batchSize, void* const* bindings) noexcept = 0; + virtual bool enqueue( + int32_t batchSize, void* const* bindings, cudaStream_t stream, cudaEvent_t* inputConsumed) noexcept + = 0; + virtual void setDebugSync(bool sync) noexcept = 0; + virtual bool getDebugSync() const noexcept = 0; + virtual void setProfiler(IProfiler*) noexcept = 0; + virtual IProfiler* getProfiler() const noexcept = 0; + virtual const ICudaEngine& getEngine() const noexcept = 0; + virtual void setName(const char* name) noexcept = 0; + virtual const char* getName() const noexcept = 0; + virtual void setDeviceMemory(void* memory) noexcept = 0; + virtual Dims getStrides(int32_t bindingIndex) const noexcept = 0; + virtual bool setOptimizationProfile(int32_t profileIndex) noexcept = 0; + virtual int32_t getOptimizationProfile() const noexcept = 0; + virtual bool setBindingDimensions(int32_t bindingIndex, Dims dimensions) noexcept = 0; + virtual Dims getBindingDimensions(int32_t bindingIndex) const noexcept = 0; + virtual bool setInputShapeBinding(int32_t bindingIndex, int32_t const* data) noexcept = 0; + virtual bool getShapeBinding(int32_t bindingIndex, int32_t* data) const noexcept = 0; + virtual bool allInputDimensionsSpecified() const noexcept = 0; + virtual bool allInputShapesSpecified() const noexcept = 0; + virtual void setErrorRecorder(IErrorRecorder* recorder) noexcept = 0; + virtual IErrorRecorder* getErrorRecorder() const noexcept = 0; + virtual bool executeV2(void* const* bindings) noexcept = 0; + virtual bool enqueueV2(void* const* bindings, cudaStream_t stream, cudaEvent_t* inputConsumed) noexcept = 0; + virtual bool setOptimizationProfileAsync(int32_t profileIndex, cudaStream_t stream) noexcept = 0; +}; + +class VTensor : public VRoot +{ +public: + virtual void setName(const char* name) noexcept = 0; + virtual const char* getName() const noexcept = 0; + virtual void setDimensions(Dims dimensions) noexcept = 0; + virtual Dims getDimensions() const noexcept = 0; + virtual void setType(DataType type) noexcept = 0; + virtual DataType getType() const noexcept = 0; + virtual bool setDynamicRange(float min, float max) noexcept = 0; + virtual bool isNetworkInput() const noexcept = 0; + virtual bool isNetworkOutput() const noexcept = 0; + virtual void setBroadcastAcrossBatch(bool broadcastAcrossBatch) noexcept = 0; + virtual bool getBroadcastAcrossBatch() const noexcept = 0; + virtual TensorLocation getLocation() const noexcept = 0; + virtual void setLocation(TensorLocation location) noexcept = 0; + virtual bool dynamicRangeIsSet() const noexcept = 0; + virtual void resetDynamicRange() noexcept = 0; + virtual float getDynamicRangeMin() const noexcept = 0; + virtual float getDynamicRangeMax() const noexcept = 0; + virtual void setAllowedFormats(TensorFormats formats) noexcept = 0; + virtual TensorFormats getAllowedFormats() const noexcept = 0; + virtual bool isShapeTensor() const noexcept = 0; + virtual bool isExecutionTensor() const noexcept = 0; +}; +class VLayer : public VRoot +{ +public: + virtual LayerType getType() const noexcept = 0; + virtual void setName(const char* name) noexcept = 0; + virtual const char* getName() const noexcept = 0; + virtual int32_t getNbInputs() const noexcept = 0; + virtual ITensor* getInput(int32_t index) const noexcept = 0; + virtual int32_t getNbOutputs() const noexcept = 0; + virtual ITensor* getOutput(int32_t index) const noexcept = 0; + virtual void setInput(int32_t index, ITensor& tensor) noexcept = 0; + virtual void setPrecision(DataType dataType) noexcept = 0; + virtual DataType getPrecision() const noexcept = 0; + virtual bool precisionIsSet() const noexcept = 0; + virtual void resetPrecision() noexcept = 0; + virtual void setOutputType(int32_t index, DataType dataType) noexcept = 0; + virtual DataType getOutputType(int32_t index) const noexcept = 0; + virtual bool outputTypeIsSet(int32_t index) const noexcept = 0; + virtual void resetOutputType(int32_t index) noexcept = 0; +}; + +class VConvolutionLayer : public VRoot +{ +public: + virtual void setKernelSize(DimsHW kernelSize) noexcept = 0; + virtual DimsHW getKernelSize() const noexcept = 0; + virtual void setNbOutputMaps(int32_t nbOutputMaps) noexcept = 0; + virtual int32_t getNbOutputMaps() const noexcept = 0; + virtual void setStride(DimsHW stride) noexcept = 0; + virtual DimsHW getStride() const noexcept = 0; + virtual void setPadding(DimsHW padding) noexcept = 0; + virtual DimsHW getPadding() const noexcept = 0; + virtual void setNbGroups(int32_t nbGroups) noexcept = 0; + virtual int32_t getNbGroups() const noexcept = 0; + virtual void setKernelWeights(Weights weights) noexcept = 0; + virtual Weights getKernelWeights() const noexcept = 0; + virtual void setBiasWeights(Weights weights) noexcept = 0; + virtual Weights getBiasWeights() const noexcept = 0; + virtual void setDilation(DimsHW dilation) noexcept = 0; + virtual DimsHW getDilation() const noexcept = 0; + virtual void setPrePadding(Dims padding) noexcept = 0; + virtual Dims getPrePadding() const noexcept = 0; + virtual void setPostPadding(Dims padding) noexcept = 0; + virtual Dims getPostPadding() const noexcept = 0; + virtual void setPaddingMode(PaddingMode paddingMode) noexcept = 0; + virtual PaddingMode getPaddingMode() const noexcept = 0; + virtual void setKernelSizeNd(Dims kernelSize) noexcept = 0; + virtual Dims getKernelSizeNd() const noexcept = 0; + virtual void setStrideNd(Dims stride) noexcept = 0; + virtual Dims getStrideNd() const noexcept = 0; + virtual void setPaddingNd(Dims padding) noexcept = 0; + virtual Dims getPaddingNd() const noexcept = 0; + virtual void setDilationNd(Dims dilation) noexcept = 0; + virtual Dims getDilationNd() const noexcept = 0; +}; + +class VFullyConnectedLayer : public VRoot +{ +public: + virtual void setNbOutputChannels(int32_t nbOutputs) noexcept = 0; + virtual int32_t getNbOutputChannels() const noexcept = 0; + virtual void setKernelWeights(Weights weights) noexcept = 0; + virtual Weights getKernelWeights() const noexcept = 0; + virtual void setBiasWeights(Weights weights) noexcept = 0; + virtual Weights getBiasWeights() const noexcept = 0; +}; + +class VActivationLayer : public VRoot +{ +public: + virtual void setActivationType(ActivationType type) noexcept = 0; + virtual ActivationType getActivationType() const noexcept = 0; + virtual void setAlpha(float alpha) noexcept = 0; + virtual void setBeta(float beta) noexcept = 0; + virtual float getAlpha() const noexcept = 0; + virtual float getBeta() const noexcept = 0; +}; + +class VPoolingLayer : public VRoot +{ +public: + virtual void setPoolingType(PoolingType type) noexcept = 0; + virtual PoolingType getPoolingType() const noexcept = 0; + virtual void setWindowSize(DimsHW windowSize) noexcept = 0; + virtual DimsHW getWindowSize() const noexcept = 0; + virtual void setStride(DimsHW stride) noexcept = 0; + virtual DimsHW getStride() const noexcept = 0; + virtual void setPadding(DimsHW padding) noexcept = 0; + virtual DimsHW getPadding() const noexcept = 0; + virtual void setBlendFactor(float blendFactor) noexcept = 0; + virtual float getBlendFactor() const noexcept = 0; + virtual void setAverageCountExcludesPadding(bool exclusive) noexcept = 0; + virtual bool getAverageCountExcludesPadding() const noexcept = 0; + virtual void setPrePadding(Dims padding) noexcept = 0; + virtual Dims getPrePadding() const noexcept = 0; + virtual void setPostPadding(Dims padding) noexcept = 0; + virtual Dims getPostPadding() const noexcept = 0; + virtual void setPaddingMode(PaddingMode paddingMode) noexcept = 0; + virtual PaddingMode getPaddingMode() const noexcept = 0; + virtual void setWindowSizeNd(Dims windowSize) noexcept = 0; + virtual Dims getWindowSizeNd() const noexcept = 0; + virtual void setStrideNd(Dims stride) noexcept = 0; + virtual Dims getStrideNd() const noexcept = 0; + virtual void setPaddingNd(Dims padding) noexcept = 0; + virtual Dims getPaddingNd() const noexcept = 0; +}; + +class VLRNLayer : public VRoot +{ +public: + virtual void setWindowSize(int32_t windowSize) noexcept = 0; + virtual int32_t getWindowSize() const noexcept = 0; + virtual void setAlpha(float alpha) noexcept = 0; + virtual float getAlpha() const noexcept = 0; + virtual void setBeta(float beta) noexcept = 0; + virtual float getBeta() const noexcept = 0; + virtual void setK(float k) noexcept = 0; + virtual float getK() const noexcept = 0; +}; + +class VScaleLayer : public VRoot +{ +public: + virtual void setMode(ScaleMode mode) noexcept = 0; + virtual ScaleMode getMode() const noexcept = 0; + virtual void setShift(Weights shift) noexcept = 0; + virtual Weights getShift() const noexcept = 0; + virtual void setScale(Weights scale) noexcept = 0; + virtual Weights getScale() const noexcept = 0; + virtual void setPower(Weights power) noexcept = 0; + virtual Weights getPower() const noexcept = 0; + virtual int32_t getChannelAxis() const noexcept = 0; + virtual void setChannelAxis(int32_t channelAxis) noexcept = 0; +}; + +class VSoftMaxLayer : public VRoot +{ +public: + virtual void setAxes(uint32_t axes) noexcept = 0; + virtual uint32_t getAxes() const noexcept = 0; +}; + +class VConcatenationLayer : public VRoot +{ +public: + virtual void setAxis(int32_t axis) noexcept = 0; + virtual int32_t getAxis() const noexcept = 0; +}; + +class VDeconvolutionLayer : public VRoot +{ +public: + virtual void setKernelSize(DimsHW kernelSize) noexcept = 0; + virtual DimsHW getKernelSize() const noexcept = 0; + virtual void setNbOutputMaps(int32_t nbOutputMaps) noexcept = 0; + virtual int32_t getNbOutputMaps() const noexcept = 0; + virtual void setStride(DimsHW stride) noexcept = 0; + virtual DimsHW getStride() const noexcept = 0; + virtual void setPadding(DimsHW padding) noexcept = 0; + virtual DimsHW getPadding() const noexcept = 0; + virtual void setNbGroups(int32_t nbGroups) noexcept = 0; + virtual int32_t getNbGroups() const noexcept = 0; + virtual void setKernelWeights(Weights weights) noexcept = 0; + virtual Weights getKernelWeights() const noexcept = 0; + virtual void setBiasWeights(Weights weights) noexcept = 0; + virtual Weights getBiasWeights() const noexcept = 0; + virtual void setPrePadding(Dims padding) noexcept = 0; + virtual Dims getPrePadding() const noexcept = 0; + virtual void setPostPadding(Dims padding) noexcept = 0; + virtual Dims getPostPadding() const noexcept = 0; + virtual void setPaddingMode(PaddingMode paddingMode) noexcept = 0; + virtual PaddingMode getPaddingMode() const noexcept = 0; + virtual void setKernelSizeNd(Dims kernelSize) noexcept = 0; + virtual Dims getKernelSizeNd() const noexcept = 0; + virtual void setStrideNd(Dims stride) noexcept = 0; + virtual Dims getStrideNd() const noexcept = 0; + virtual void setPaddingNd(Dims padding) noexcept = 0; + virtual Dims getPaddingNd() const noexcept = 0; + virtual void setDilationNd(Dims dilation) noexcept = 0; + virtual Dims getDilationNd() const noexcept = 0; +}; + +class VElementWiseLayer : public VRoot +{ +public: + virtual void setOperation(ElementWiseOperation op) noexcept = 0; + virtual ElementWiseOperation getOperation() const noexcept = 0; +}; + +class VGatherLayer : public VRoot +{ +public: + virtual void setGatherAxis(int32_t axis) noexcept = 0; + virtual int32_t getGatherAxis() const noexcept = 0; + virtual void setNbElementWiseDims(int32_t k) noexcept = 0; + virtual int32_t getNbElementWiseDims() const noexcept = 0; +}; + +class VRNNv2Layer : public VRoot +{ +public: + virtual int32_t getLayerCount() const noexcept = 0; + virtual int32_t getHiddenSize() const noexcept = 0; + virtual int32_t getMaxSeqLength() const noexcept = 0; + virtual int32_t getDataLength() const noexcept = 0; + virtual void setSequenceLengths(ITensor& seqLengths) noexcept = 0; + virtual ITensor* getSequenceLengths() const noexcept = 0; + virtual void setOperation(RNNOperation op) noexcept = 0; + virtual RNNOperation getOperation() const noexcept = 0; + virtual void setInputMode(RNNInputMode op) noexcept = 0; + virtual RNNInputMode getInputMode() const noexcept = 0; + virtual void setDirection(RNNDirection op) noexcept = 0; + virtual RNNDirection getDirection() const noexcept = 0; + virtual void setWeightsForGate(int32_t layerIndex, RNNGateType gate, bool isW, Weights weights) noexcept = 0; + virtual Weights getWeightsForGate(int32_t layerIndex, RNNGateType gate, bool isW) const noexcept = 0; + virtual void setBiasForGate(int32_t layerIndex, RNNGateType gate, bool isW, Weights bias) noexcept = 0; + virtual Weights getBiasForGate(int32_t layerIndex, RNNGateType gate, bool isW) const noexcept = 0; + virtual void setHiddenState(ITensor& hidden) noexcept = 0; + virtual ITensor* getHiddenState() const noexcept = 0; + virtual void setCellState(ITensor& cell) noexcept = 0; + virtual ITensor* getCellState() const noexcept = 0; +}; + +class VPluginLayer : public VRoot +{ +public: + virtual IPlugin& getPlugin() noexcept = 0; +}; + +class VPluginV2Layer : public VRoot +{ +public: + virtual IPluginV2& getPlugin() noexcept = 0; +}; + +class VUnaryLayer : public VRoot +{ +public: + virtual void setOperation(UnaryOperation op) noexcept = 0; + virtual UnaryOperation getOperation() const noexcept = 0; +}; + +class VReduceLayer : public VRoot +{ +public: + virtual void setOperation(ReduceOperation op) noexcept = 0; + virtual ReduceOperation getOperation() const noexcept = 0; + virtual void setReduceAxes(uint32_t reduceAxes) noexcept = 0; + virtual uint32_t getReduceAxes() const noexcept = 0; + virtual void setKeepDimensions(bool keepDimensions) noexcept = 0; + virtual bool getKeepDimensions() const noexcept = 0; +}; + +class VPaddingLayer : public VRoot +{ +public: + virtual void setPrePadding(DimsHW padding) noexcept = 0; + virtual DimsHW getPrePadding() const noexcept = 0; + virtual void setPostPadding(DimsHW padding) noexcept = 0; + virtual DimsHW getPostPadding() const noexcept = 0; + virtual void setPrePaddingNd(Dims padding) noexcept = 0; + virtual Dims getPrePaddingNd() const noexcept = 0; + virtual void setPostPaddingNd(Dims padding) noexcept = 0; + virtual Dims getPostPaddingNd() const noexcept = 0; +}; + +class VShuffleLayer : public VRoot +{ +public: + virtual void setFirstTranspose(const Permutation& permutation) noexcept = 0; + virtual const Permutation& getFirstTranspose() const noexcept = 0; + virtual void setReshapeDimensions(Dims dimensions) noexcept = 0; + virtual Dims getReshapeDimensions() const noexcept = 0; + virtual void setSecondTranspose(const Permutation& permutation) noexcept = 0; + virtual const Permutation& getSecondTranspose() const noexcept = 0; + virtual void setZeroIsPlaceholder(bool zeroIsPlaceholder) = 0; + virtual bool getZeroIsPlaceholder() const = 0; +}; + +class VSliceLayer : public VRoot +{ +public: + virtual void setStart(Dims start) noexcept = 0; + virtual Dims getStart() const noexcept = 0; + virtual void setSize(Dims size) noexcept = 0; + virtual Dims getSize() const noexcept = 0; + virtual void setStride(Dims stride) noexcept = 0; + virtual Dims getStride() const noexcept = 0; + virtual void setMode(SliceMode mode) noexcept = 0; + virtual SliceMode getMode() const noexcept = 0; +}; + +class VShapeLayer : public VRoot +{ +public: +}; + +class VTopKLayer : public VRoot +{ +public: + virtual void setOperation(TopKOperation op) noexcept = 0; + virtual TopKOperation getOperation() const noexcept = 0; + virtual void setK(int32_t k) noexcept = 0; + virtual int32_t getK() const noexcept = 0; + virtual void setReduceAxes(uint32_t reduceAxes) noexcept = 0; + virtual uint32_t getReduceAxes() const noexcept = 0; +}; + +class VMatrixMultiplyLayer : public VRoot +{ +public: + virtual void setOperation(int32_t index, MatrixOperation op) noexcept = 0; + virtual MatrixOperation getOperation(int32_t index) const noexcept = 0; +}; + +class VRaggedSoftMaxLayer : public VRoot +{ +public: +}; + +class VIdentityLayer : public VRoot +{ +public: +}; + +class VConstantLayer : public VRoot +{ +public: + virtual void setWeights(Weights weights) noexcept = 0; + virtual Weights getWeights() const noexcept = 0; + virtual void setDimensions(Dims dimensions) noexcept = 0; + virtual Dims getDimensions() const noexcept = 0; +}; + +class VParametricReLULayer : public VRoot +{ +public: +}; + +class VResizeLayer : public VRoot +{ +public: + virtual void setOutputDimensions(Dims dimensions) noexcept = 0; + virtual Dims getOutputDimensions() const noexcept = 0; + virtual void setScales(const float* scales, int32_t nbScales) noexcept = 0; + virtual int32_t getScales(int32_t size, float* scales) const noexcept = 0; + virtual void setResizeMode(ResizeMode resizeMode) noexcept = 0; + virtual ResizeMode getResizeMode() const noexcept = 0; + virtual void setAlignCorners(bool alignCorners) noexcept = 0; + virtual bool getAlignCorners() const noexcept = 0; + virtual void setCoordinateTransformation(ResizeCoordinateTransformation coordTransform) noexcept = 0; + virtual ResizeCoordinateTransformation getCoordinateTransformation() const noexcept = 0; + virtual void setSelectorForSinglePixel(ResizeSelector selector) noexcept = 0; + virtual ResizeSelector getSelectorForSinglePixel() const noexcept = 0; + virtual void setNearestRounding(ResizeRoundMode value) noexcept = 0; + virtual ResizeRoundMode getNearestRounding() const noexcept = 0; +}; + +class VLoopBoundaryLayer : public VRoot +{ +public: + virtual ILoop* getLoop() const noexcept = 0; +}; + +class VRecurrenceLayer : public VRoot +{ +public: +}; + +class VLoopOutputLayer : public VRoot +{ +public: + virtual LoopOutput getLoopOutput() const noexcept = 0; + virtual void setAxis(int32_t axis) noexcept = 0; + virtual int32_t getAxis() const noexcept = 0; +}; + +class VTripLimitLayer : public VRoot +{ +public: + virtual TripLimit getTripLimit() const noexcept = 0; +}; + +class VIteratorLayer : public VRoot +{ +public: + virtual void setAxis(int32_t axis) noexcept = 0; + virtual int32_t getAxis() const noexcept = 0; + virtual void setReverse(bool reverse) noexcept = 0; + virtual bool getReverse() const noexcept = 0; +}; +class VLoop : public VRoot +{ +public: + virtual IRecurrenceLayer* addRecurrence(ITensor& initialValue) noexcept = 0; + virtual ITripLimitLayer* addTripLimit(ITensor& tensor, TripLimit limit) noexcept = 0; + virtual IIteratorLayer* addIterator(ITensor& tensor, int32_t axis = 0, bool reverse = false) noexcept = 0; + virtual ILoopOutputLayer* addLoopOutput(ITensor& tensor, LoopOutput outputKind, int32_t axis = 0) noexcept = 0; + virtual void setName(const char* name) noexcept = 0; + virtual const char* getName() const noexcept = 0; +}; +class VSelectLayer : public VRoot +{ +}; + +class VFillLayer : public VRoot +{ +public: + virtual void setDimensions(Dims dimensions) noexcept = 0; + virtual Dims getDimensions() const noexcept = 0; + virtual void setOperation(FillOperation op) noexcept = 0; + virtual FillOperation getOperation() const noexcept = 0; + virtual void setAlpha(double alpha) noexcept = 0; + virtual double getAlpha() const noexcept = 0; + virtual void setBeta(double beta) noexcept = 0; + virtual double getBeta() const noexcept = 0; +}; + +class VQuantizeLayer : public VRoot +{ +public: + virtual int32_t getAxis() const noexcept = 0; + virtual void setAxis(int32_t axis) noexcept = 0; +}; + +class VDequantizeLayer : public VRoot +{ +public: + virtual int32_t getAxis() const noexcept = 0; + virtual void setAxis(int32_t axis) noexcept = 0; +}; + +class VNetworkDefinition : public VRoot +{ +public: + virtual ITensor* addInput(const char* name, DataType type, Dims dimensions) noexcept = 0; + virtual void markOutput(ITensor& tensor) noexcept = 0; + virtual IConvolutionLayer* addConvolution( + ITensor& input, int32_t nbOutputMaps, DimsHW kernelSize, Weights kernelWeights, Weights biasWeights) noexcept + = 0; + virtual IFullyConnectedLayer* addFullyConnected( + ITensor& input, int32_t nbOutputs, Weights kernelWeights, Weights biasWeights) noexcept + = 0; + virtual IActivationLayer* addActivation(ITensor& input, ActivationType type) noexcept = 0; + virtual IPoolingLayer* addPooling(ITensor& input, PoolingType type, DimsHW windowSize) noexcept = 0; + virtual ILRNLayer* addLRN(ITensor& input, int32_t window, float alpha, float beta, float k) noexcept = 0; + virtual IScaleLayer* addScale(ITensor& input, ScaleMode mode, Weights shift, Weights scale, Weights power) noexcept + = 0; + virtual ISoftMaxLayer* addSoftMax(ITensor& input) noexcept = 0; + virtual IConcatenationLayer* addConcatenation(ITensor* const* inputs, int32_t nbInputs) noexcept = 0; + virtual IDeconvolutionLayer* addDeconvolution( + ITensor& input, int32_t nbOutputMaps, DimsHW kernelSize, Weights kernelWeights, Weights biasWeights) noexcept + = 0; + virtual IElementWiseLayer* addElementWise(ITensor& input1, ITensor& input2, ElementWiseOperation op) noexcept = 0; + virtual IUnaryLayer* addUnary(ITensor& input, UnaryOperation operation) noexcept = 0; + virtual IPaddingLayer* addPadding(ITensor& input, DimsHW prePadding, DimsHW postPadding) noexcept = 0; + virtual IShuffleLayer* addShuffle(ITensor& input) noexcept = 0; + virtual int32_t getNbLayers() const noexcept = 0; + virtual ILayer* getLayer(int32_t index) const noexcept = 0; + virtual int32_t getNbInputs() const noexcept = 0; + virtual ITensor* getInput(int32_t index) const noexcept = 0; + virtual int32_t getNbOutputs() const noexcept = 0; + virtual ITensor* getOutput(int32_t index) const noexcept = 0; + virtual IReduceLayer* addReduce( + ITensor& input, ReduceOperation operation, uint32_t reduceAxes, bool keepDimensions) noexcept + = 0; + virtual ITopKLayer* addTopK(ITensor& input, TopKOperation op, int32_t k, uint32_t reduceAxes) noexcept = 0; + virtual IGatherLayer* addGather(ITensor& data, ITensor& indices, int32_t axis) noexcept = 0; + virtual IRaggedSoftMaxLayer* addRaggedSoftMax(ITensor& input, ITensor& bounds) noexcept = 0; + virtual IMatrixMultiplyLayer* addMatrixMultiply( + ITensor& input0, MatrixOperation op0, ITensor& input1, MatrixOperation op1) noexcept + = 0; + virtual IConstantLayer* addConstant(Dims dimensions, Weights weights) noexcept = 0; + virtual IRNNv2Layer* addRNNv2( + ITensor& input, int32_t layerCount, int32_t hiddenSize, int32_t maxSeqLen, RNNOperation op) noexcept + = 0; + virtual IIdentityLayer* addIdentity(ITensor& input) noexcept = 0; + virtual void removeTensor(ITensor& tensor) noexcept = 0; + virtual void unmarkOutput(ITensor& tensor) noexcept = 0; + virtual IPluginV2Layer* addPluginV2(ITensor* const* inputs, int32_t nbInputs, IPluginV2& plugin) noexcept = 0; + virtual ISliceLayer* addSlice(ITensor& input, Dims start, Dims size, Dims stride) noexcept = 0; + virtual void setName(const char* name) noexcept = 0; + virtual const char* getName() const noexcept = 0; + virtual IShapeLayer* addShape(ITensor& input) noexcept = 0; + virtual bool hasImplicitBatchDimension() const noexcept = 0; + virtual bool markOutputForShapes(ITensor& tensor) noexcept = 0; + virtual bool unmarkOutputForShapes(ITensor& tensor) noexcept = 0; + virtual IParametricReLULayer* addParametricReLU(ITensor& input, ITensor& slope) noexcept = 0; + virtual IConvolutionLayer* addConvolutionNd( + ITensor& input, int32_t nbOutputMaps, Dims kernelSize, Weights kernelWeights, Weights biasWeights) noexcept + = 0; + virtual IPoolingLayer* addPoolingNd(ITensor& input, PoolingType type, Dims windowSize) noexcept = 0; + virtual IDeconvolutionLayer* addDeconvolutionNd( + ITensor& input, int32_t nbOutputMaps, Dims kernelSize, Weights kernelWeights, Weights biasWeights) noexcept + = 0; + virtual IScaleLayer* addScaleNd( + ITensor& input, ScaleMode mode, Weights shift, Weights scale, Weights power, int32_t channelAxis) noexcept + = 0; + virtual IResizeLayer* addResize(ITensor& input) noexcept = 0; + virtual bool hasExplicitPrecision() const noexcept = 0; + virtual ILoop* addLoop() noexcept = 0; + virtual ISelectLayer* addSelect(ITensor& condition, ITensor& thenInput, ITensor& elseInput) noexcept = 0; + virtual IFillLayer* addFill(Dims dimensions, FillOperation op) noexcept = 0; + virtual IPaddingLayer* addPaddingNd(ITensor& input, Dims prePadding, Dims postPadding) noexcept = 0; + virtual bool setWeightsName(Weights weights, const char* name) noexcept = 0; + virtual void setErrorRecorder(IErrorRecorder* recorder) noexcept = 0; + virtual IErrorRecorder* getErrorRecorder() const noexcept = 0; + virtual IDequantizeLayer* addDequantize(ITensor& input, ITensor& scale) noexcept = 0; + virtual IQuantizeLayer* addQuantize(ITensor& input, ITensor& scale) noexcept = 0; +}; + +class VAlgorithmIOInfo : public VRoot +{ +public: + virtual TensorFormat getTensorFormat() const noexcept = 0; + virtual DataType getDataType() const noexcept = 0; + virtual Dims getStrides() const noexcept = 0; +}; + +class VAlgorithmVariant : public VRoot +{ +public: + virtual int64_t getImplementation() const noexcept = 0; + virtual int64_t getTactic() const noexcept = 0; +}; + +class VAlgorithmContext : public VRoot +{ +public: + virtual const char* getName() const noexcept = 0; + virtual Dims getDimensions(int32_t index, OptProfileSelector select) const noexcept = 0; + virtual int32_t getNbInputs() const noexcept = 0; + virtual int32_t getNbOutputs() const noexcept = 0; +}; + +class VAlgorithm : public VRoot +{ +public: + virtual const IAlgorithmIOInfo& getAlgorithmIOInfo(int32_t index) const noexcept = 0; + virtual const IAlgorithmVariant& getAlgorithmVariant() const noexcept = 0; + virtual float getTimingMSec() const noexcept = 0; + virtual std::size_t getWorkspaceSize() const noexcept = 0; + virtual const IAlgorithmIOInfo* getAlgorithmIOInfoByIndex(int32_t index) const noexcept = 0; +}; + +class VTimingCache : public VRoot +{ +public: + virtual nvinfer1::IHostMemory* serialize() const noexcept = 0; + virtual bool combine(const ITimingCache& inputCache, bool ignoreMismatch) noexcept = 0; + virtual bool reset() noexcept = 0; +}; + +class VBuilderConfig : public VRoot +{ +public: + virtual void setMinTimingIterations(int32_t minTiming) noexcept = 0; + virtual int32_t getMinTimingIterations() const noexcept = 0; + virtual void setAvgTimingIterations(int32_t avgTiming) noexcept = 0; + virtual int32_t getAvgTimingIterations() const noexcept = 0; + virtual void setEngineCapability(EngineCapability capability) noexcept = 0; + virtual EngineCapability getEngineCapability() const noexcept = 0; + virtual void setInt8Calibrator(IInt8Calibrator* calibrator) noexcept = 0; + virtual IInt8Calibrator* getInt8Calibrator() const noexcept = 0; + virtual void setMaxWorkspaceSize(std::size_t workspaceSize) noexcept = 0; + virtual std::size_t getMaxWorkspaceSize() const noexcept = 0; + virtual void setFlags(BuilderFlags builderFlags) noexcept = 0; + virtual BuilderFlags getFlags() const noexcept = 0; + virtual void clearFlag(BuilderFlag builderFlag) noexcept = 0; + virtual void setFlag(BuilderFlag builderFlag) noexcept = 0; + virtual bool getFlag(BuilderFlag builderFlag) const noexcept = 0; + virtual void setDeviceType(const ILayer* layer, DeviceType deviceType) noexcept = 0; + virtual DeviceType getDeviceType(const ILayer* layer) const noexcept = 0; + virtual bool isDeviceTypeSet(const ILayer* layer) const noexcept = 0; + virtual void resetDeviceType(const ILayer* layer) noexcept = 0; + virtual bool canRunOnDLA(const ILayer* layer) const noexcept = 0; + virtual void setDLACore(int32_t dlaCore) noexcept = 0; + virtual int32_t getDLACore() const noexcept = 0; + virtual void setDefaultDeviceType(DeviceType deviceType) noexcept = 0; + virtual DeviceType getDefaultDeviceType() const noexcept = 0; + virtual void reset() noexcept = 0; + virtual void setProfileStream(const cudaStream_t stream) noexcept = 0; + virtual cudaStream_t getProfileStream() const noexcept = 0; + virtual int32_t addOptimizationProfile(const IOptimizationProfile* profile) noexcept = 0; + virtual int32_t getNbOptimizationProfiles() const noexcept = 0; + virtual void setProfilingVerbosity(ProfilingVerbosity verbosity) noexcept = 0; + virtual ProfilingVerbosity getProfilingVerbosity() const noexcept = 0; + virtual void setAlgorithmSelector(IAlgorithmSelector* selector) noexcept = 0; + virtual IAlgorithmSelector* getAlgorithmSelector() const noexcept = 0; + virtual bool setCalibrationProfile(const IOptimizationProfile* profile) noexcept = 0; + virtual const IOptimizationProfile* getCalibrationProfile() noexcept = 0; + virtual void setQuantizationFlags(QuantizationFlags flags) noexcept = 0; + virtual QuantizationFlags getQuantizationFlags() const noexcept = 0; + virtual void clearQuantizationFlag(QuantizationFlag flag) noexcept = 0; + virtual void setQuantizationFlag(QuantizationFlag flag) noexcept = 0; + virtual bool getQuantizationFlag(QuantizationFlag flag) const noexcept = 0; + virtual bool setTacticSources(TacticSources tacticSources) noexcept = 0; + virtual TacticSources getTacticSources() const noexcept = 0; + virtual nvinfer1::ITimingCache* createTimingCache(const void* blob, std::size_t size) const noexcept = 0; + virtual bool setTimingCache(const ITimingCache& cache, bool ignoreMismatch) noexcept = 0; + virtual const nvinfer1::ITimingCache* getTimingCache() const noexcept = 0; +}; + +class VBuilder : public VRoot +{ +public: + virtual void setMaxBatchSize(int32_t batchSize) noexcept = 0; + virtual int32_t getMaxBatchSize() const noexcept = 0; + virtual bool platformHasFastFp16() const noexcept = 0; + virtual bool platformHasFastInt8() const noexcept = 0; + virtual int32_t getMaxDLABatchSize() const noexcept = 0; + virtual int32_t getNbDLACores() const noexcept = 0; + virtual void setGpuAllocator(IGpuAllocator* allocator) noexcept = 0; + virtual nvinfer1::IBuilderConfig* createBuilderConfig() noexcept = 0; + virtual nvinfer1::ICudaEngine* buildEngineWithConfig(INetworkDefinition& network, IBuilderConfig& config) noexcept + = 0; + virtual nvinfer1::INetworkDefinition* createNetworkV2(NetworkDefinitionCreationFlags flags) noexcept = 0; + virtual nvinfer1::IOptimizationProfile* createOptimizationProfile() noexcept = 0; + virtual void setErrorRecorder(IErrorRecorder* recorder) noexcept = 0; + virtual IErrorRecorder* getErrorRecorder() const noexcept = 0; + virtual void reset() noexcept = 0; + virtual bool platformHasTf32() const noexcept = 0; + virtual nvinfer1::IHostMemory* buildSerializedNetwork(INetworkDefinition& network, IBuilderConfig& config) noexcept + = 0; + virtual bool isNetworkSupported(INetworkDefinition const& network, IBuilderConfig const& config) const noexcept + = 0; +}; + +} // namespace apiv +} // namespace nvinfer1 + +#endif // NV_INFER_RUNTIME_IMPL_H diff --git a/include/NvInferLegacyDims.h b/include/NvInferLegacyDims.h new file mode 100644 index 00000000..ee4c588c --- /dev/null +++ b/include/NvInferLegacyDims.h @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ + +#ifndef NV_INFER_LEGACY_DIMS_H +#define NV_INFER_LEGACY_DIMS_H + +#include "NvInferRuntimeCommon.h" + +//! +//! \file NvInferLegacyDims.h +//! +//! This file contains declarations of legacy dimensions types which use channel +//! semantics in their names, and declarations on which those types rely. +//! + +//! +//! \namespace nvinfer1 +//! +//! \brief The TensorRT API version 1 namespace. +//! +namespace nvinfer1 +{ +//! +//! \class Dims2 +//! \brief Descriptor for two-dimensional data. +//! +class Dims2 : public Dims +{ +public: + //! + //! \brief Construct an empty Dims2 object. + //! + Dims2() + { + nbDims = 2; + for (int32_t i = 0; i < MAX_DIMS; ++i) + { + d[i] = 0; + } + } + + //! + //! \brief Construct a Dims2 from 2 elements. + //! + //! \param d0 The first element. + //! \param d1 The second element. + //! + Dims2(int32_t d0, int32_t d1) + { + nbDims = 2; + d[0] = d0; + d[1] = d1; + for (int32_t i = nbDims; i < MAX_DIMS; ++i) + { + d[i] = 0; + } + } +}; + +//! +//! \class DimsHW +//! \brief Descriptor for two-dimensional spatial data. +//! +class DimsHW : public Dims2 +{ +public: + //! + //! \brief Construct an empty DimsHW object. + //! + DimsHW() + : Dims2() + { + } + + //! + //! \brief Construct a DimsHW given height and width. + //! + //! \param height the height of the data + //! \param width the width of the data + //! + DimsHW(int32_t height, int32_t width) + : Dims2(height, width) + { + } + + //! + //! \brief Get the height. + //! + //! \return The height. + //! + int32_t& h() + { + return d[0]; + } + + //! + //! \brief Get the height. + //! + //! \return The height. + //! + int32_t h() const + { + return d[0]; + } + + //! + //! \brief Get the width. + //! + //! \return The width. + //! + int32_t& w() + { + return d[1]; + } + + //! + //! \brief Get the width. + //! + //! \return The width. + //! + int32_t w() const + { + return d[1]; + } +}; + +//! +//! \class Dims3 +//! \brief Descriptor for three-dimensional data. +//! +class Dims3 : public Dims +{ +public: + //! + //! \brief Construct an empty Dims3 object. + //! + Dims3() + { + nbDims = 3; + for (int32_t i = 0; i < MAX_DIMS; ++i) + { + d[i] = 0; + } + } + + //! + //! \brief Construct a Dims3 from 3 elements. + //! + //! \param d0 The first element. + //! \param d1 The second element. + //! \param d2 The third element. + //! + Dims3(int32_t d0, int32_t d1, int32_t d2) + { + nbDims = 3; + d[0] = d0; + d[1] = d1; + d[2] = d2; + for (int32_t i = nbDims; i < MAX_DIMS; ++i) + { + d[i] = 0; + } + } +}; + +//! +//! \class Dims4 +//! \brief Descriptor for four-dimensional data. +//! +class Dims4 : public Dims +{ +public: + //! + //! \brief Construct an empty Dims4 object. + //! + Dims4() + { + nbDims = 4; + for (int32_t i = 0; i < MAX_DIMS; ++i) + { + d[i] = 0; + } + } + + //! + //! \brief Construct a Dims4 from 4 elements. + //! + //! \param d0 The first element. + //! \param d1 The second element. + //! \param d2 The third element. + //! \param d3 The fourth element. + //! + Dims4(int32_t d0, int32_t d1, int32_t d2, int32_t d3) + { + nbDims = 4; + d[0] = d0; + d[1] = d1; + d[2] = d2; + d[3] = d3; + for (int32_t i = nbDims; i < MAX_DIMS; ++i) + { + d[i] = 0; + } + } +}; + +} // namespace nvinfer1 + +#endif // NV_INFER_LEGCY_DIMS_H diff --git a/include/NvInferPlugin.h b/include/NvInferPlugin.h index f06d3caa..639a552d 100644 --- a/include/NvInferPlugin.h +++ b/include/NvInferPlugin.h @@ -30,8 +30,9 @@ extern "C" //! //! \brief Initialize and register all the existing TensorRT plugins to the Plugin Registry with an optional //! namespace. The plugin library author should ensure that this function name is unique to the library. This - //! function should be called once before accessing the Plugin Registry. \param logger Logger object to print plugin - //! registration information \param libNamespace Namespace used to register all the plugins in this library + //! function should be called once before accessing the Plugin Registry. + //! \param logger Logger object to print plugin registration information + //! \param libNamespace Namespace used to register all the plugins in this library //! TENSORRTAPI bool initLibNvInferPlugins(void* logger, const char* libNamespace); diff --git a/include/NvInferPluginUtils.h b/include/NvInferPluginUtils.h index 1f22522b..f9c94555 100644 --- a/include/NvInferPluginUtils.h +++ b/include/NvInferPluginUtils.h @@ -16,6 +16,7 @@ #ifndef NV_INFER_PLUGIN_UTILS_H #define NV_INFER_PLUGIN_UTILS_H + #include "NvInferRuntimeCommon.h" //! @@ -27,34 +28,6 @@ namespace nvinfer1 { -//! -//! \enum PluginType -//! -//! \brief The type values for the various plugins. -//! -//! \see INvPlugin::getPluginType() -//! -enum class PluginType : int32_t -{ - kFASTERRCNN = 0, //!< FasterRCNN fused plugin (RPN + ROI pooling). - kNORMALIZE = 1, //!< Normalize plugin. - kPERMUTE = 2, //!< Permute plugin. - kPRIORBOX = 3, //!< PriorBox plugin. - kSSDDETECTIONOUTPUT = 4, //!< SSD DetectionOutput plugin. - kCONCAT = 5, //!< Concat plugin. - kPRELU = 6, //!< YOLO PReLU Plugin. - kYOLOREORG = 7, //!< YOLO Reorg Plugin. - kYOLOREGION = 8, //!< YOLO Region Plugin. - kANCHORGENERATOR = 9, //!< SSD Grid Anchor Generator. -}; - -//! Maximum number of elements in PluginType enum. \see PluginType -template <> -constexpr inline int32_t EnumMax() -{ - return 10; -} - namespace plugin { @@ -75,7 +48,7 @@ typedef struct //! \param minSize Minimum box size in pixels. Can not be nullptr. //! \param maxSize Maximum box size in pixels. Can be nullptr. //! \param aspectRatios Aspect ratios of the boxes. Can be nullptr. -//! \param numMinSize Number of element in minSize. Must be larger than 0. +//! \param numMinSize Number of elements in minSize. Must be larger than 0. //! \param numMaxSize Number of elements in maxSize. Can be 0 or same as numMinSize. //! \param numAspectRatios Number of elements in aspectRatios. Can be 0. //! \param flip If true, will flip each aspect ratio. For example, if there is an aspect ratio "r", the aspect ratio @@ -106,8 +79,10 @@ struct PriorBoxParameters //! \param poolingH Height of the output in pixels after ROI pooling on feature map. //! \param poolingW Width of the output in pixels after ROI pooling on feature map. //! \param featureStride Feature stride; ratio of input image size to feature map size. Assuming that max pooling layers -//! in neural network use square filters. \param preNmsTop Number of proposals to keep before applying NMS. \param -//! nmsMaxOut Number of remaining proposals after applying NMS. \param anchorsRatioCount Number of anchor box ratios. +//! in the neural network use square filters. +//! \param preNmsTop Number of proposals to keep before applying NMS. +//! \param nmsMaxOut Number of remaining proposals after applying NMS. +//! \param anchorsRatioCount Number of anchor box ratios. //! \param anchorsScaleCount Number of anchor box scales. //! \param iouThreshold IoU (Intersection over Union) threshold used for the NMS step. //! \param minBoxSize Minimum allowed bounding box size before scaling, used for anchor box calculation. @@ -162,7 +137,7 @@ enum class CodeTypeSSD : int32_t //! //! \brief The DetectionOutput plugin layer generates the detection output based on location and confidence predictions by doing non maximum suppression. -//! This plugin first decodes the bounding boxes based on the anchors generated. It then performs non_max_suppression on the decoded bouding boxes. +//! This plugin first decodes the bounding boxes based on the anchors generated. It then performs non_max_suppression on the decoded bounding boxes. //! DetectionOutputParameters defines a set of parameters for creating the DetectionOutput plugin layer. //! It contains: //! \param shareLocation If true, bounding box are shared among different classes. @@ -192,22 +167,9 @@ struct DetectionOutputParameters }; //! -//! \brief The Region plugin layer performs region proposal calculation: generate 5 bounding boxes per cell (for -//! yolo9000, generate 3 bounding boxes per cell). +//! \brief When performing yolo9000, softmaxTree is helping to do softmax on confidence scores, for element to get the precise classification through word-tree structured classification definition. //! -//! For each box, calculating its probablities of objects detections from 80 pre-defined classifications (yolo9000 -//! has 9418 pre-defined classifications, and these 9418 items are organized as work-tree structure). -//! RegionParameters defines a set of parameters for creating the Region plugin layer. -//! -//! \param num Number of predicted bounding box for each grid cell. -//! \param coords Number of coordinates for a bounding box. -//! \param classes Number of classfications to be predicted. -//! \param softmaxTree When performing yolo9000, softmaxTree is helping to do softmax on confidence scores, for element -//! to get the precise classfication through word-tree structured classfication definition. -//! -//! \deprecated. This plugin is superseded by createRegionPlugin and will be removed in TensorRT 8.0. -//! -TRT_DEPRECATED typedef struct +struct softmaxTree { int32_t* leaf; int32_t n; @@ -219,9 +181,19 @@ TRT_DEPRECATED typedef struct int32_t groups; int32_t* groupSize; int32_t* groupOffset; -} softmaxTree; // softmax tree +}; -struct TRT_DEPRECATED RegionParameters +//! +//! \brief The Region plugin layer performs region proposal calculation: generate 5 bounding boxes per cell (for yolo9000, generate 3 bounding boxes per cell). +//! For each box, calculating its probablities of objects detections from 80 pre-defined classifications (yolo9000 has 9418 pre-defined classifications, +//! and these 9418 items are organized as work-tree structure). +//! RegionParameters defines a set of parameters for creating the Region plugin layer. +//! \param num Number of predicted bounding box for each grid cell. +//! \param coords Number of coordinates for a bounding box. +//! \param classes Number of classifications to be predicted. +//! \param smTree Helping structure to do softmax on confidence scores. +//! +struct RegionParameters { int32_t num; int32_t coords; @@ -254,6 +226,7 @@ struct NMSParameters bool isNormalized; }; -} // end plugin namespace -} // end nvinfer1 namespace -#endif +} // namespace plugin +} // namespace nvinfer1 + +#endif // NV_INFER_PLUGIN_UTILS_H diff --git a/include/NvInferRuntime.h b/include/NvInferRuntime.h index 644f6388..6709a109 100644 --- a/include/NvInferRuntime.h +++ b/include/NvInferRuntime.h @@ -23,44 +23,86 @@ //! This is the top-level API file for TensorRT extended runtime library. //! +#include "NvInferImpl.h" #include "NvInferRuntimeCommon.h" namespace nvinfer1 { class IExecutionContext; //!< Forward declaration of IExecutionContext for use by other interfaces. -class ICudaEngine; //!< Forward declaration of ICudaENgine for use by other interfaces. -class IPluginFactory; //!< Forward declaration of IPluginFactory for use by other interfaces. +class ICudaEngine; //!< Forward declaration of ICudaEngine for use by other interfaces. +class IPluginFactory; //!< Forward declaration of IPluginFactory for use by other interfaces. + +//! +//! \class INoCopy +//! +//! \brief Base class for all TensorRT interfaces that are implemented by the TensorRT libraries +//! +//! Objects of such classes are not movable or copyable, and should only be manipulated +//! via pointers. +//! + +class INoCopy +{ +protected: + INoCopy() = default; + virtual ~INoCopy() = default; + INoCopy(const INoCopy& other) = delete; + INoCopy& operator=(const INoCopy& other) = delete; + INoCopy(INoCopy&& other) = delete; + INoCopy& operator=(INoCopy&& other) = delete; +}; //! //! \enum EngineCapability //! //! \brief List of supported engine capability flows. //! -//! The EngineCapability determines the restrictions of a network during build time for what can be executed -//! at runtime. EngineCapability::kDEFAULT does not provide any restrictions on functionality and the -//! resulting serialized engine can be executed with TensorRT's standard runtime APIs in the nvinfer1 namespace. -//! EngineCapabiltiy::kSAFE_GPU provides a restricted subset of network operations that are safety certified and -//! the resulting serialized engine can be executed with TensorRT's safe runtime APIs in the nvinfer1::safe namespace. -//! EngineCapability::kSAFE_DLA provides a restricted subset of network operations that are DLA compatible and -//! the resulting serialized engine can be executed using NvMediaDLA's runtime APIs. See sampleNvmedia for an -//! example of integrating NvMediaDLA APIs with TensorRT APIs. +//! \details The EngineCapability determines the restrictions of a network during build time and what runtime +//! it targets. When BuilderFlag::kSAFETY_SCOPE is not set (by default), EngineCapability::kSTANDARD does not provide +//! any restrictions on functionality and the resulting serialized engine can be executed with TensorRT's standard +//! runtime APIs in the nvinfer1 namespace. EngineCapability::kSAFETY provides a restricted subset of network +//! operations that are safety certified and the resulting serialized engine can be executed with TensorRT's safe +//! runtime APIs in the nvinfer1::safe namespace. EngineCapability::kDLA_STANDALONE provides a restricted subset of +//! network operations that are DLA compatible and the resulting serialized engine can be executed using standalone +//! DLA runtime APIs. See sampleNvmedia for an example of integrating NvMediaDLA APIs with TensorRT APIs. //! + enum class EngineCapability : int32_t { - kDEFAULT = 0, //!< Full capability, TensorRT mode without any restrictions using TensorRT nvinfer1 APIs. - kSAFE_GPU = 1, //!< Safety restricted capability, TensorRT flow that can only run on GPU devices via TensorRT - //!< nvinfer1::safe APIs. - kSAFE_DLA = 2, //!< Safety restricted capability, TensorRT flow that can only run on DLA devices via - //!< NvMediaDLA APIs. + //! + //! Standard: TensorRT flow without targeting the safety runtime. + //! This flow supports both DeviceType::kGPU and DeviceType::kDLA. + //! + kSTANDARD = 0, + kDEFAULT TRT_DEPRECATED_ENUM = kSTANDARD, + + //! + //! Safety: TensorRT flow with restrictions targeting the safety runtime. + //! See safety documentation for list of supported layers and formats. + //! This flow supports only DeviceType::kGPU. + //! + kSAFETY = 1, + kSAFE_GPU TRT_DEPRECATED_ENUM = kSAFETY, + + //! + //! DLA Standalone: TensorRT flow with restrictions targeting external, to TensorRT, DLA runtimes. + //! See DLA documentation for list of supported layers and formats. + //! This flow supports only DeviceType::kDLA. + //! + kDLA_STANDALONE = 2, + kSAFE_DLA TRT_DEPRECATED_ENUM = kDLA_STANDALONE, }; +namespace impl +{ //! Maximum number of elements in EngineCapability enum. \see EngineCapability template <> -constexpr inline int32_t EnumMax() +struct EnumMaxImpl { - return 3; -} + static constexpr int32_t kVALUE = 3; +}; +} // namespace impl //! //! \class Weights @@ -92,194 +134,42 @@ public: //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. //! -class IHostMemory +class IHostMemory : public INoCopy { public: - virtual void* data() const noexcept = 0; //!< A pointer to the raw data that is owned by the library. - virtual std::size_t size() const noexcept = 0; //!< The size in bytes of the data that was allocated. - virtual DataType type() const noexcept = 0; //!< The type of the memory that was allocated. - virtual void destroy() noexcept = 0; //!< Destroy the allocated memory. -protected: - virtual ~IHostMemory() {} -}; + virtual ~IHostMemory() noexcept = default; -//! \class IPlugin -//! -//! \brief Plugin class for user-implemented layers. -//! -//! Plugins are a mechanism for applications to implement custom layers. Each plugin is owned by the application, and its lifetime -//! must span any use of it by TensorRT -//! -class IPlugin -{ -public: - //! - //! \brief Get the number of outputs from the layer. - //! - //! \return The number of outputs. - //! - //! This function is called by the implementations of INetworkDefinition and IBuilder. In particular, it is called - //! prior to any call to initialize(). - //! - virtual int32_t getNbOutputs() const TRTNOEXCEPT = 0; - - //! - //! \brief Get the dimension of an output tensor. - //! - //! \param index The index of the output tensor. - //! \param inputs The input tensors. - //! \param nbInputDims The number of input tensors. - //! - //! This function is called by the implementations of INetworkDefinition and IBuilder. In particular, it is called - //! prior to any call to initialize(). - //! - virtual Dims getOutputDimensions(int32_t index, const Dims* inputs, int32_t nbInputDims) TRTNOEXCEPT = 0; - - //! - //! \brief Configure the layer. - //! - //! This function is called by the builder prior to initialize(). It provides an opportunity for the layer to make - //! algorithm choices on the basis of its weights, dimensions, and maximum batch size. The type is assumed to be - //! FP32 and format NCHW. - //! - //! \param inputDims The input tensor dimensions. - //! \param nbInputs The number of inputs. - //! \param outputDims The output tensor dimensions. - //! \param nbOutputs The number of outputs. - //! \param maxBatchSize The maximum batch size. - //! - //! The dimensions passed here do not include the outermost batch size (i.e. for 2-D image networks, they will be - //! 3-dimensional CHW dimensions). - //! - //! This method is not called for PluginExt classes, configureWithFormat is called instead. - //! - virtual void configure(const Dims* inputDims, int32_t nbInputs, const Dims* outputDims, int32_t nbOutputs, - int32_t maxBatchSize) TRTNOEXCEPT = 0; - - //! - //! \brief Initialize the layer for execution. This is called when the engine is created. - //! - //! \return 0 for success, else non-zero (which will cause engine termination). - //! - virtual int32_t initialize() TRTNOEXCEPT = 0; - - //! - //! \brief Release resources acquired during plugin layer initialization. This is called when the engine is - //! destroyed. \see initialize() - //! - virtual void terminate() TRTNOEXCEPT = 0; - - //! - //! \brief Find the workspace size required by the layer. - //! - //! This function is called during engine startup, after initialize(). The workspace size returned should be - //! sufficient for any batch size up to the maximum. - //! - //! \return The workspace size. - //! - virtual size_t getWorkspaceSize(int32_t maxBatchSize) const TRTNOEXCEPT = 0; - - //! - //! \brief Execute the layer. - //! - //! \param batchSize The number of inputs in the batch. - //! \param inputs The memory for the input tensors. - //! \param outputs The memory for the output tensors. - //! \param workspace Workspace for execution. - //! \param stream The stream in which to execute the kernels. - //! - //! \return 0 for success, else non-zero (which will cause engine termination). - //! - virtual int32_t enqueue(int32_t batchSize, const void* const* inputs, void** outputs, void* workspace, - cudaStream_t stream) TRTNOEXCEPT = 0; - - //! - //! \brief Find the size of the serialization buffer required. - //! - //! \return The size of the serialization buffer. - //! - virtual size_t getSerializationSize() TRTNOEXCEPT = 0; - - //! - //! \brief Serialize the layer. - //! - //! \param buffer A pointer to a buffer of size at least that returned by getSerializationSize(). - //! - //! \see getSerializationSize() - //! - virtual void serialize(void* buffer) TRTNOEXCEPT = 0; - - virtual ~IPlugin() {} -}; - -//! -//! \class IPluginExt -//! -//! \brief Plugin class for user-implemented layers. -//! -//! Plugins are a mechanism for applications to implement custom layers. Each plugin is owned by the application, and its lifetime -//! must span any use of it by TensorRT. -//! -class IPluginExt : public IPlugin -{ -public: - //! - //! \brief Return the API version with which this plugin was built. - //! - //! Do not override this method as it is used by the TensorRT library to maintain backwards-compatibility with - //! plugins. - //! - virtual int32_t getTensorRTVersion() const TRTNOEXCEPT + //! A pointer to the raw data that is owned by the library. + void* data() const noexcept { - return NV_TENSORRT_VERSION; + return mImpl->data(); } - //! - //! \brief Check format support. - //! - //! \param type DataType requested. - //! \param format PluginFormat requested. - //! \return true if the plugin supports the type-format combination. - //! - //! This function is called by the implementations of INetworkDefinition, IBuilder, and ICudaEngine. - //! In particular, it is called when creating an engine and when deserializing an engine. - //! - //! \warning DataType:kBOOL not supported. - //! - virtual bool supportsFormat(DataType type, PluginFormat format) const TRTNOEXCEPT = 0; + //! The size in bytes of the data that was allocated. + std::size_t size() const noexcept + { + return mImpl->size(); + } + //! The type of the memory that was allocated. + DataType type() const noexcept + { + return mImpl->type(); + } //! - //! \brief Configure the layer. + //! Destroy the allocated memory. //! - //! This function is called by the builder prior to initialize(). It provides an opportunity for the layer to make - //! algorithm choices on the basis of its weights, dimensions, and maximum batch size. + //! \deprecated Deprecated interface will be removed in TensorRT 10.0. //! - //! \param inputDims The input tensor dimensions. - //! \param nbInputs The number of inputs. - //! \param outputDims The output tensor dimensions. - //! \param nbOutputs The number of outputs. - //! \param type The data type selected for the engine. - //! \param format The format selected for the engine. - //! \param maxBatchSize The maximum batch size. + //! \warning Calling destroy on a managed pointer will result in a double-free error. //! - //! The dimensions passed here do not include the outermost batch size (i.e. for 2-D image networks, they will be - //! 3-dimensional CHW dimensions). - //! - //! \warning DataType:kBOOL not supported. - //! - virtual void configureWithFormat(const Dims* inputDims, int32_t nbInputs, const Dims* outputDims, int32_t nbOutputs, - DataType type, PluginFormat format, int32_t maxBatchSize) TRTNOEXCEPT = 0; - - virtual ~IPluginExt() {} + TRT_DEPRECATED void destroy() noexcept + { + delete this; + } protected: - //! - //! \brief Derived classes should not implement this. In a C++11 API it would be override final. - //! - void configure(const Dims* /*inputDims*/, int32_t /*nbInputs*/, const Dims* /*outputDims*/, int32_t /*nbOutputs*/, - int32_t /*maxBatchSize*/) _TENSORRT_FINAL TRTNOEXCEPT - { - } + apiv::VHostMemory* mImpl; }; //! @@ -307,11 +197,31 @@ enum class DimensionOperation : int32_t //! Maximum number of elements in DimensionOperation enum. \see DimensionOperation template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 9; } +//! +//! \enum TensorLocation +//! \brief The location for tensor data storage, device or host. +//! +enum class TensorLocation : int32_t +{ + kDEVICE = 0, //!< Data stored on device. + kHOST = 1, //!< Data stored on host. +}; + +namespace impl +{ +//! Maximum number of elements in TensorLocation enum. \see TensorLocation +template <> +struct EnumMaxImpl +{ + static constexpr int32_t kVALUE = 2; +}; +} // namespace impl + //! //! \class IDimensionExpr //! @@ -320,20 +230,29 @@ constexpr inline int32_t EnumMax() //! in overrides of IPluginV2DynamicExt::getOutputDimensions to define output //! dimensions in terms of input dimensions. //! +//! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. +//! //! \see DimensionOperation, IPluginV2DynamicExt::getOutputDimensions //! -class IDimensionExpr +class IDimensionExpr : public INoCopy { public: //! Return true if expression is a build-time constant. - virtual bool isConstant() const = 0; + bool isConstant() const noexcept + { + return mImpl->isConstant(); + } //! If isConstant(), returns value of the constant. //! If !isConstant(), return std::numeric_limits::min(). - virtual int32_t getConstantValue() const = 0; + int32_t getConstantValue() const noexcept + { + return mImpl->getConstantValue(); + } protected: - virtual ~IDimensionExpr() {} + apiv::VDimensionExpr* mImpl; + virtual ~IDimensionExpr() noexcept = default; }; //! @@ -353,18 +272,26 @@ protected: //! //! \see IDimensionExpr //! -class IExprBuilder +class IExprBuilder : public INoCopy { public: //! Return pointer to IDimensionExp for given value. - virtual const IDimensionExpr* constant(int32_t value) = 0; + const IDimensionExpr* constant(int32_t value) noexcept + { + return mImpl->constant(value); + } //! Return pointer to IDimensionExp that represents the given operation applied to first and second. //! Returns nullptr if op is not a valid DimensionOperation. - virtual const IDimensionExpr* operation(DimensionOperation op, const IDimensionExpr& first, const IDimensionExpr& second) = 0; + const IDimensionExpr* operation( + DimensionOperation op, const IDimensionExpr& first, const IDimensionExpr& second) noexcept + { + return mImpl->operation(op, first, second); + } protected: - virtual ~IExprBuilder() {} + apiv::VExprBuilder* mImpl; + virtual ~IExprBuilder() noexcept = default; }; //! @@ -403,12 +330,12 @@ struct DynamicPluginTensorDesc //! //! Clients should override the public methods, including the following inherited methods: //! -//! virtual int32_t getNbOutputs() const TRTNOEXCEPT = 0; +//! virtual int32_t getNbOutputs() const noexcept = 0; //! virtual nvinfer1::DataType getOutputDataType(int32_t index, const nvinfer1::DataType* inputTypes, int32_t -//! nbInputs) const TRTNOEXCEPT = 0; virtual size_t getSerializationSize() const TRTNOEXCEPT = 0; virtual void -//! serialize(void* buffer) const TRTNOEXCEPT = 0; virtual void destroy() TRTNOEXCEPT = 0; virtual void -//! setPluginNamespace(const char* pluginNamespace) TRTNOEXCEPT = 0; virtual const char* getPluginNamespace() const -//! TRTNOEXCEPT = 0; +//! nbInputs) const noexcept = 0; virtual size_t getSerializationSize() const noexcept = 0; virtual void +//! serialize(void* buffer) const noexcept = 0; virtual void destroy() noexcept = 0; virtual void +//! setPluginNamespace(const char* pluginNamespace) noexcept = 0; virtual const char* getPluginNamespace() const +//! noexcept = 0; //! //! For getOutputDataType, the inputTypes will always be DataType::kFLOAT or DataType::kINT32, //! and the returned type is canonicalized to DataType::kFLOAT if it is DataType::kHALF or DataType:kINT8. @@ -417,14 +344,14 @@ struct DynamicPluginTensorDesc class IPluginV2DynamicExt : public nvinfer1::IPluginV2Ext { public: - IPluginV2DynamicExt* clone() const _TENSORRT_OVERRIDE TRTNOEXCEPT = 0; + IPluginV2DynamicExt* clone() const noexcept override = 0; //! //! \brief Get expressions for computing dimensions of an output tensor from dimensions of the input tensors. //! //! \param outputIndex The index of the output tensor //! \param inputs Expressions for dimensions of the input tensors - //! \param nbInputDims The number of input tensors + //! \param nbInputs The number of input tensors //! \param exprBuilder Object for generating new expressions //! //! This function is called by the implementations of IBuilder during analysis of the network. @@ -444,7 +371,7 @@ public: //! return output; //! virtual DimsExprs getOutputDimensions( - int32_t outputIndex, const DimsExprs* inputs, int32_t nbInputs, IExprBuilder& exprBuilder) + int32_t outputIndex, const DimsExprs* inputs, int32_t nbInputs, IExprBuilder& exprBuilder) noexcept = 0; //! @@ -485,15 +412,41 @@ public: //! Warning: TensorRT will stop asking for formats once it finds kFORMAT_COMBINATION_LIMIT on combinations. //! virtual bool supportsFormatCombination( - int32_t pos, const PluginTensorDesc* inOut, int32_t nbInputs, int32_t nbOutputs) TRTNOEXCEPT = 0; + int32_t pos, const PluginTensorDesc* inOut, int32_t nbInputs, int32_t nbOutputs) noexcept + = 0; //! - //! \brief Configure the layer. + //! \brief Configure the plugin. //! - //! This function is called by the builder prior to initialize(). It provides an opportunity for the layer to make - //! algorithm choices on the basis of bounds on the input and output tensors, and the target value. + //! configurePlugin() can be called multiple times in both the build and execution phases. The build phase happens + //! before initialize() is called and only occurs during creation of an engine by IBuilder. The execution phase + //! happens after initialize() is called and occurs during both creation of an engine by IBuilder and execution + //! of an engine by IExecutionContext. //! - //! This function is also called once when the resource requirements are changed based on the optimization profiles. + //! Build phase: + //! IPluginV2DynamicExt->configurePlugin is called when a plugin is being prepared for profiling but not for any + //! specific input size. This provides an opportunity for the plugin to make algorithmic choices on the basis of + //! input and output formats, along with the bound of possible dimensions. The min and max value of the + //! DynamicPluginTensorDesc correspond to the kMIN and kMAX value of the current profile that the plugin is being + //! profiled for, with the desc.dims field corresponding to the dimensions of plugin specified at network creation. + //! Wildcard dimensions will exist during this phase in the desc.dims field. + //! + //! Execution phase: + //! IPluginV2DynamicExt->configurePlugin is called when a plugin is being prepared for executing the plugin for a + //! specific dimensions. This provides an opportunity for the plugin to change algorithmic choices based on the + //! explicit input dimensions stored in desc.dims field. + //! * IBuilder will call this function once per profile, with desc.dims resolved to the values specified by the + //! kOPT + //! field of the current profile. Wildcard dimensions will not exist during this phase. + //! * IExecutionContext will call this during the next subsequent instance enqueue[V2]() or execute[V2]() if: + //! - The batch size is changed from previous call of execute()/enqueue() if hasImplicitBatchDimension() returns + //! true. + //! - The optimization profile is changed via setOptimizationProfile() or setOptimizationProfileAsync(). + //! - An input shape binding is changed via setInputShapeBinding(). + //! - An input execution binding is changed via setBindingDimensions(). + //! \warning The execution phase is timing critical during IExecutionContext but is not part of the timing loop when + //! called from IBuilder. Performance bottlenecks of configurePlugin won't show up during engine building but will + //! be visible during execution after calling functions that trigger layer resource updates. //! //! \param in The input tensors attributes that are used for configuration. //! \param nbInputs Number of input tensors. @@ -501,7 +454,8 @@ public: //! \param nbOutputs Number of output tensors. //! virtual void configurePlugin(const DynamicPluginTensorDesc* in, int32_t nbInputs, - const DynamicPluginTensorDesc* out, int32_t nbOutputs) TRTNOEXCEPT = 0; + const DynamicPluginTensorDesc* out, int32_t nbOutputs) noexcept + = 0; //! //! \brief Find the workspace size required by the layer. @@ -513,7 +467,8 @@ public: //! \return The workspace size. //! virtual size_t getWorkspaceSize(const PluginTensorDesc* inputs, int32_t nbInputs, const PluginTensorDesc* outputs, - int32_t nbOutputs) const TRTNOEXCEPT = 0; + int32_t nbOutputs) const noexcept + = 0; //! //! \brief Execute the layer. @@ -528,125 +483,58 @@ public: //! \return 0 for success, else non-zero (which will cause engine termination). //! virtual int32_t enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) TRTNOEXCEPT = 0; + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept + = 0; protected: - int32_t getTensorRTVersion() const _TENSORRT_OVERRIDE TRTNOEXCEPT + //! + //! \brief Return the API version with which this plugin was built. The + //! upper byte reserved by TensorRT and is used to differentiate this from IPluginV2. + //! + //! Do not override this method as it is used by the TensorRT library to maintain backwards-compatibility with + //! plugins. + //! + int32_t getTensorRTVersion() const noexcept override { return (static_cast(PluginVersion::kV2_DYNAMICEXT) << 24 | (NV_TENSORRT_VERSION & 0xFFFFFF)); } - virtual ~IPluginV2DynamicExt() {} + virtual ~IPluginV2DynamicExt() noexcept {} - // Rest of the methods below are obsolete inherited methods, and marked final when using a C++11 compiler. - // Derived classes should not override them. +private: + // Following are obsolete base class methods, and must not be implemented or used. - //! - //! \brief Derived classes should not implement this. In a C++11 API it would be override final. - //! - //! Instead, derived classes should override the overload of getOutputDimensions that returns DimsExprs. - //! - //! \deprecated Deprecated interface will be removed in TensorRT 8.0. - //! - TRT_DEPRECATED - Dims getOutputDimensions( - int32_t /*index*/, const Dims* /*inputs*/, int32_t /*nbInputDims*/) _TENSORRT_FINAL TRTNOEXCEPT + void configurePlugin(Dims const*, int32_t, Dims const*, int32_t, DataType const*, DataType const*, bool const*, + bool const*, PluginFormat, int32_t) noexcept override final { - return Dims{-1, {}, {}}; } - //! - //! \brief Derived classes should not implement this. In a C++11 API it would be override final. - //! - //! This method is not used because with dynamic shapes there is no implicit batch dimension to broadcast across. - //! - //! \deprecated Deprecated interface will be removed in TensorRT 8.0. - //! - TRT_DEPRECATED - bool isOutputBroadcastAcrossBatch(int32_t /*outputIndex*/, const bool* /*inputIsBroadcasted*/, - int32_t /*nbInputs*/) const _TENSORRT_FINAL TRTNOEXCEPT + bool supportsFormat(DataType, PluginFormat) const noexcept override final { return false; } - //! - //! \brief Derived classes should not implement this. In a C++11 API it would be override final. - //! - //! This method is not used because with dynamic shapes there is no implicit batch dimension to broadcast across. - //! - //! \deprecated Deprecated interface will be removed in TensorRT 8.0. - //! - TRT_DEPRECATED - bool canBroadcastInputAcrossBatch(int32_t /*inputIndex*/) const _TENSORRT_FINAL TRTNOEXCEPT + Dims getOutputDimensions(int32_t, Dims const*, int32_t) noexcept override final + { + return Dims{-1, {}}; + } + + bool isOutputBroadcastAcrossBatch(int32_t, bool const*, int32_t) const noexcept override final + { + return false; + } + + bool canBroadcastInputAcrossBatch(int32_t) const noexcept override final { return true; } - //! - //! \brief Derived classes should not implement this. In a C++11 API it would be override final. - //! - //! This method is not used because it does not allow a plugin to specify mixed formats. - //! - //! Instead, derived classes should override supportsFormatCombination, which allows plugins - //! to express mixed formats. - //! - //! \deprecated Deprecated interface will be removed in TensorRT 8.0. - //! - TRT_DEPRECATED - bool supportsFormat(DataType /*type*/, PluginFormat /*format*/) const _TENSORRT_FINAL TRTNOEXCEPT - { - return false; - } - - //! - //! \brief Derived classes should not implement this. In a C++11 API it would be override final. - //! - //! This method is not used because tensors with dynamic shapes do not have an implicit batch dimension, - //! input dimensions might be variable, and outputs might have different floating-point formats. - //! - //! Instead, derived classes should override the overload of configurePlugin that takes poiners to - //! DynamicPluginTensorDesc. - //! - //! \deprecated Deprecated interface will be removed in TensorRT 8.0. - //! - TRT_DEPRECATED - void configurePlugin(const Dims* /*inputDims*/, int32_t /*nbInputs*/, const Dims* /*outputDims*/, - int32_t /*nbOutputs*/, const DataType* /*inputTypes*/, const DataType* /*outputTypes*/, - const bool* /*inputIsBroadcast*/, const bool* /*outputIsBroadcast*/, PluginFormat /*floatFormat*/, - int32_t /*maxBatchSize*/) _TENSORRT_FINAL TRTNOEXCEPT - { - } - - //! - //! \brief Derived classes should not implement this. In a C++11 API it would be override final. - //! - //! This method is not used because tensors with dynamic shapes do not have an implicit batch dimension, - //! and the other dimensions might not be build-time constants. - //! - //! Instead, derived classes should override the overload of getWorkspaceSize that takes pointers to - //! PluginTensorDesc. The arguments to that overload provide maximum bounds on all dimensions. - //! - //! \deprecated Deprecated interface will be removed in TensorRT 8.0. - //! - TRT_DEPRECATED - size_t getWorkspaceSize(int32_t /*maxBatchSize*/) const _TENSORRT_FINAL TRTNOEXCEPT + size_t getWorkspaceSize(int32_t) const noexcept override final { return 0; } - //! - //! \brief Derived classes should not implement this. In a C++11 API it would be override final. - //! - //! This method is not used because tensors with dynamic shapes can have different sizes in different execution - //! contexts. - //! - //! Instead, derived classes should override the overload of enqueue that takes pointers to PluginTensorDesc. - //! - //! \deprecated Deprecated interface will be removed in TensorRT 8.0. - //! - TRT_DEPRECATED - int32_t enqueue(int32_t /*batchSize*/, const void* const* /*inputs*/, void** /*outputs*/, void* /*workspace*/, - cudaStream_t /*stream*/) _TENSORRT_FINAL TRTNOEXCEPT + int32_t enqueue(int32_t, const void* const*, void* const*, void*, cudaStream_t) noexcept override final { return 1; } @@ -671,9 +559,9 @@ public: //! \param layerName The name of the layer, set when constructing the network definition. //! \param ms The time in milliseconds to execute the layer. //! - virtual void reportLayerTime(const char* layerName, float ms) TRTNOEXCEPT = 0; + virtual void reportLayerTime(const char* layerName, float ms) noexcept = 0; - virtual ~IProfiler() {} + virtual ~IProfiler() noexcept {} }; //! @@ -689,13 +577,14 @@ enum class WeightsRole : int32_t kSHIFT = 2, //!< shift part of IScaleLayer kSCALE = 3, //!< scale part of IScaleLayer kCONSTANT = 4, //!< weights for IConstantLayer + kANY = 5, //!< Any other weights role }; //! Maximum number of elements in WeightsRole enum. \see WeightsRole template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { - return 5; + return 6; } //! @@ -711,7 +600,7 @@ enum class DeviceType : int32_t //! Maximum number of elements in DeviceType enum. \see DeviceType template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 2; } @@ -723,19 +612,31 @@ constexpr inline int32_t EnumMax() //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. //! -class IRuntime +class IRuntime : public INoCopy { public: + virtual ~IRuntime() noexcept = default; + //! //! \brief Deserialize an engine from a stream. //! + //! If an error recorder has been set for the runtime, it will also be passed to the engine. + //! //! \param blob The memory that holds the serialized engine. - //! \param size The size of the memory. + //! \param size The size of the memory in bytes. //! \param pluginFactory The plugin factory, if any plugins are used by the network, otherwise nullptr. //! //! \return The engine, or nullptr if it could not be deserialized. //! - virtual nvinfer1::ICudaEngine* deserializeCudaEngine(const void* blob, std::size_t size, IPluginFactory* pluginFactory) noexcept = 0; + //! \deprecated Deprecated interface will be removed in TensorRT 10.0. + //! + //! \warning IPluginFactory is no longer supported, therefore pluginFactory must be a nullptr. + //! + TRT_DEPRECATED nvinfer1::ICudaEngine* deserializeCudaEngine( + const void* blob, std::size_t size, IPluginFactory* pluginFactory) noexcept + { + return mImpl->deserializeCudaEngine(blob, size, nullptr); + } //! //! \brief Set the DLA core that the deserialized engine must execute on. @@ -744,7 +645,10 @@ public: //! //! \warning Starting with TensorRT 8, the default value will be -1 if the DLA is not specified or unused. //! - virtual void setDLACore(int32_t dlaCore) noexcept = 0; + void setDLACore(int32_t dlaCore) noexcept + { + mImpl->setDLACore(dlaCore); + } //! //! \brief Get the DLA core that the engine executes on. @@ -752,31 +656,44 @@ public: //! //! \warning Starting with TensorRT 8, the default value will be -1 if the DLA is not specified or unused. //! - virtual int32_t getDLACore() const noexcept = 0; + int32_t getDLACore() const noexcept + { + return mImpl->getDLACore(); + } //! //! \brief Returns number of DLA hardware cores accessible. //! - virtual int32_t getNbDLACores() const noexcept = 0; + int32_t getNbDLACores() const noexcept + { + return mImpl->getNbDLACores(); + } //! //! \brief Destroy this object. //! - virtual void destroy() noexcept = 0; + //! \deprecated Deprecated interface will be removed in TensorRT 10.0. + //! + //! \warning Calling destroy on a managed pointer will result in a double-free error. + //! + TRT_DEPRECATED void destroy() noexcept + { + delete this; + } -protected: - virtual ~IRuntime() {} - -public: //! //! \brief Set the GPU allocator. - //! \param allocator Set the GPU allocator to be used by the runtime. All GPU memory acquired will use this allocator. If NULL is passed, the default allocator will be used. + //! \param allocator Set the GPU allocator to be used by the runtime. All GPU memory acquired will use this + //! allocator. If NULL is passed, the default allocator will be used. //! //! Default: uses cudaMalloc/cudaFree. //! //! If nullptr is passed, the default allocator will be used. //! - virtual void setGpuAllocator(IGpuAllocator* allocator) noexcept = 0; + void setGpuAllocator(IGpuAllocator* allocator) noexcept + { + mImpl->setGpuAllocator(allocator); + } //! //! \brief Set the ErrorRecorder for this interface @@ -786,26 +703,34 @@ public: //! recorder to nullptr unregisters the recorder with the interface, resulting in a call to decRefCount if //! a recorder has been registered. //! + //! If an error recorder is not set, messages will be sent to the global log stream. + //! //! \param recorder The error recorder to register with this interface. // - //! \see getErrorRecorder + //! \see getErrorRecorder() //! - virtual void setErrorRecorder(IErrorRecorder* recorder) noexcept = 0; + void setErrorRecorder(IErrorRecorder* recorder) noexcept + { + mImpl->setErrorRecorder(recorder); + } //! //! \brief get the ErrorRecorder assigned to this interface. //! - //! Retrieves the assigned error recorder object for the given class. A default error recorder does not exist, - //! so a nullptr will be returned if setErrorRecorder has not been called. + //! Retrieves the assigned error recorder object for the given class. A nullptr will be returned if + //! an error handler has not been set. //! //! \return A pointer to the IErrorRecorder object that has been registered. //! - //! \see setErrorRecorder + //! \see setErrorRecorder() //! - virtual IErrorRecorder* getErrorRecorder() const noexcept = 0; + IErrorRecorder* getErrorRecorder() const noexcept + { + return mImpl->getErrorRecorder(); + } //! - //! \brief Deserialize an engine from a stream when plugin factory is not used. + //! \brief Deserialize an engine from a stream. //! //! \param blob The memory that holds the serialized engine. //! \param size The size of the memory. @@ -814,8 +739,11 @@ public: //! nvinfer1::ICudaEngine* deserializeCudaEngine(const void* blob, std::size_t size) noexcept { - return deserializeCudaEngine(blob, size, nullptr); + return mImpl->deserializeCudaEngine(blob, size, nullptr); } + +protected: + apiv::VRuntime* mImpl; }; //! @@ -825,9 +753,11 @@ public: //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. //! -class IRefitter +class IRefitter : public INoCopy { public: + virtual ~IRefitter() noexcept = default; + //! //! \brief Specify new weights for a layer of given name. //! Returns true on success, or false if new weights are rejected. @@ -838,14 +768,25 @@ public: //! * The number of weights is inconsistent with the layer’s original specification. //! //! Modifying the weights before method refit() completes will result in undefined behavior. - virtual bool setWeights(const char* layerName, WeightsRole role, Weights weights) TRTNOEXCEPT = 0; + bool setWeights(const char* layerName, WeightsRole role, Weights weights) noexcept + { + return mImpl->setWeights(layerName, role, weights); + } //! //! \brief Updates associated engine. Return true if successful. //! //! Failure occurs if getMissing() != 0 before the call. //! - virtual bool refitCudaEngine() TRTNOEXCEPT = 0; + //! The behavior is undefined if the engine has pending enqueued work. + //! + //! Extant IExecutionContexts associated with the engine should not be used afterwards. + //! Instead, create new IExecutionContexts after refitting. + //! + bool refitCudaEngine() noexcept + { + return mImpl->refitCudaEngine(); + } //! //! \brief Get description of missing weights. @@ -861,9 +802,12 @@ public: //! \return The number of missing Weights. //! //! If layerNames!=nullptr, each written pointer points to a string owned by - //! the engine being refitted, and becomes invalid when the engine is destroyed. + //! the engine being refit, and becomes invalid when the engine is destroyed. //! - virtual int32_t getMissing(int32_t size, const char** layerNames, WeightsRole* roles) TRTNOEXCEPT = 0; + int32_t getMissing(int32_t size, const char** layerNames, WeightsRole* roles) noexcept + { + return mImpl->getMissing(size, layerNames, roles); + } //! //! \brief Get description of all weights that could be refit. @@ -875,16 +819,23 @@ public: //! \return The number of Weights that could be refit. //! //! If layerNames!=nullptr, each written pointer points to a string owned by - //! the engine being refitted, and becomes invalid when the engine is destroyed. + //! the engine being refit, and becomes invalid when the engine is destroyed. //! - virtual int32_t getAll(int32_t size, const char** layerNames, WeightsRole* roles) TRTNOEXCEPT = 0; + int32_t getAll(int32_t size, const char** layerNames, WeightsRole* roles) noexcept + { + return mImpl->getAll(size, layerNames, roles); + } - virtual void destroy() TRTNOEXCEPT = 0; + //! + //! \deprecated Deprecated interface will be removed in TensorRT 10.0. + //! + //! \warning Calling destroy on a managed pointer will result in a double-free error. + //! + TRT_DEPRECATED void destroy() noexcept + { + delete this; + } -protected: - virtual ~IRefitter() {} - -public: //! //! Update dynamic range for a tensor. //! @@ -897,7 +848,10 @@ public: //! Returns false if there is no Int8 engine tensor derived from //! a network tensor of that name. If successful, then getMissing //! may report that some weights need to be supplied. - virtual bool setDynamicRange(const char* tensorName, float min, float max) TRTNOEXCEPT = 0; + bool setDynamicRange(const char* tensorName, float min, float max) noexcept + { + return mImpl->setDynamicRange(tensorName, min, max); + } //! //! \brief Get minimum of dynamic range. @@ -906,7 +860,10 @@ public: //! //! If the dynamic range was never set, returns the minimum computed during calibration. //! - virtual float getDynamicRangeMin(const char* tensorName) const TRTNOEXCEPT = 0; + float getDynamicRangeMin(const char* tensorName) const noexcept + { + return mImpl->getDynamicRangeMin(tensorName); + } //! //! \brief Get maximum of dynamic range. @@ -915,7 +872,10 @@ public: //! //! If the dynamic range was never set, returns the maximum computed during calibration. //! - virtual float getDynamicRangeMax(const char* tensorName) const TRTNOEXCEPT = 0; + float getDynamicRangeMax(const char* tensorName) const noexcept + { + return mImpl->getDynamicRangeMax(tensorName); + } //! //! \brief Get names of all tensors that have refittable dynamic ranges. @@ -926,9 +886,12 @@ public: //! \return The number of Weights that could be refit. //! //! If tensorNames!=nullptr, each written pointer points to a string owned by - //! the engine being refitted, and becomes invalid when the engine is destroyed. + //! the engine being refit, and becomes invalid when the engine is destroyed. //! - virtual int32_t getTensorsWithDynamicRange(int32_t size, const char** tensorNames) const TRTNOEXCEPT = 0; + int32_t getTensorsWithDynamicRange(int32_t size, const char** tensorNames) const noexcept + { + return mImpl->getTensorsWithDynamicRange(size, tensorNames); + } //! //! \brief Set the ErrorRecorder for this interface @@ -938,51 +901,88 @@ public: //! recorder to nullptr unregisters the recorder with the interface, resulting in a call to decRefCount if //! a recorder has been registered. //! + //! If an error recorder is not set, messages will be sent to the global log stream. + //! //! \param recorder The error recorder to register with this interface. // - //! \see getErrorRecorder + //! \see getErrorRecorder() //! - virtual void setErrorRecorder(IErrorRecorder* recorder) TRTNOEXCEPT = 0; + void setErrorRecorder(IErrorRecorder* recorder) noexcept + { + mImpl->setErrorRecorder(recorder); + } //! - //! \brief get the ErrorRecorder assigned to this interface. + //! \brief Get the ErrorRecorder assigned to this interface. //! - //! Retrieves the assigned error recorder object for the given class. A default error recorder does not exist, - //! so a nullptr will be returned if setErrorRecorder has not been called. + //! Retrieves the assigned error recorder object for the given class. A nullptr will be returned if + //! an error handler has not been set. //! //! \return A pointer to the IErrorRecorder object that has been registered. //! - //! \see setErrorRecorder + //! \see setErrorRecorder() //! - virtual IErrorRecorder* getErrorRecorder() const TRTNOEXCEPT = 0; -}; + IErrorRecorder* getErrorRecorder() const noexcept + { + return mImpl->getErrorRecorder(); + } -//! -//! \class IPluginFactory -//! -//! \brief Plugin factory for deserialization. -//! -//! This Interface is guaranteed not to change for the same major version of TensorRT. -class IPluginFactory -{ -public: //! - //! \brief Create a plugin from serialized data. + //! \brief Specify new weights of given name. //! - //! Responsibility of destroying this plugin lies with the application. - //! It can be done anytime after consumers of this plugin are destroyed. + //! \param name The name of the weights to be refit. + //! \param weights The new weights to associate with the name. //! - //! \param layerName The name of the layer. - //! \param serialData The serialized data. - //! \param serialLength The length of the serialized data. + //! Returns true on success, or false if new weights are rejected. + //! Possible reasons for rejection are: //! - //! \return The plugin. + //! * The name of weights is nullptr or does not correspond to any refittable weights. + //! * The number of weights is inconsistent with the original specification. //! - //! \see IPlugin::serialize() - //! - virtual IPlugin* createPlugin(const char* layerName, const void* serialData, size_t serialLength) TRTNOEXCEPT = 0; + //! Modifying the weights before method refitCudaEngine() completes will result in undefined behavior. + bool setNamedWeights(const char* name, Weights weights) noexcept + { + return mImpl->setNamedWeights(name, weights); + } - virtual ~IPluginFactory() {} + //! + //! \brief Get names of missing weights. + //! + //! For example, if some Weights have been set, but the engine was optimized + //! in a way that combines weights, any unsupplied Weights in the combination + //! are considered missing. + //! + //! \param size The number of weights names that can be safely written to. + //! \param weightsNames The names of the weights to be updated, or nullptr for unnamed weights. + //! + //! \return The number of missing Weights. + //! + //! If layerNames!=nullptr, each written pointer points to a string owned by + //! the engine being refit, and becomes invalid when the engine is destroyed. + //! + int32_t getMissingWeights(int32_t size, const char** weightsNames) noexcept + { + return mImpl->getMissingWeights(size, weightsNames); + } + + //! + //! \brief Get names of all weights that could be refit. + //! + //! \param size The number of weights names that can be safely written to. + //! \param weightsNames The names of the weights to be updated, or nullptr for unnamed weights. + //! + //! \return The number of Weights that could be refit. + //! + //! If layerNames!=nullptr, each written pointer points to a string owned by + //! the engine being refit, and becomes invalid when the engine is destroyed. + //! + int32_t getAllWeights(int32_t size, const char** weightsNames) noexcept + { + return mImpl->getAllWeights(size, weightsNames); + } + +protected: + apiv::VRefitter* mImpl; }; //! @@ -1004,7 +1004,7 @@ enum class OptProfileSelector : int32_t //!< Number of different values of OptProfileSelector enum. \see OptProfileSelector template <> -constexpr inline int32_t EnumMax() +constexpr inline int32_t EnumMax() noexcept { return 3; } @@ -1031,7 +1031,7 @@ constexpr inline int32_t EnumMax() //! //! \see IBuilderConfig::addOptimizationProfile() //! -class IOptimizationProfile +class IOptimizationProfile : public INoCopy { public: //! @@ -1059,14 +1059,20 @@ public: //! //! \warning If run on DLA, minimum, optimum, and maximum dimensions must to be the same. //! - virtual bool setDimensions(const char* inputName, OptProfileSelector select, Dims dims) noexcept = 0; + bool setDimensions(const char* inputName, OptProfileSelector select, Dims dims) noexcept + { + return mImpl->setDimensions(inputName, select, dims); + } //! //! \brief Get the minimum / optimum / maximum dimensions for a dynamic input tensor. //! //! If the dimensions have not been previously set via setDimensions(), return an invalid Dims with nbDims == -1. //! - virtual Dims getDimensions(const char* inputName, OptProfileSelector select) const noexcept = 0; + Dims getDimensions(const char* inputName, OptProfileSelector select) const noexcept + { + return mImpl->getDimensions(inputName, select); + } //! //! \brief Set the minimum / optimum / maximum values for an input shape tensor. @@ -1075,10 +1081,25 @@ public: //! This implies that the datatype of t is DataType::kINT32, the rank is either 0 or 1, and the dimensions of t //! are fixed at network definition time. This function must not be called for any input tensor that is not a //! shape tensor. + //! //! Each time this function is called for the same input tensor, the same nbValues must be supplied (either 1 //! if the tensor rank is 0, or dims.d[0] if the rank is 1). Furthermore, if minVals, optVals, maxVals are the //! minimum, optimum, and maximum values, it must be true that minVals[i] <= optVals[i] <= maxVals[i] for - //! i = 0, ..., nbValues - 1. + //! i = 0, ..., nbValues - 1. Execution of the network must be valid for the optVals. + //! + //! Shape tensors are tensors that contribute to shape calculations in some way, and can contain + //! any int32_t values appropriate for the network. Examples: + //! + //! * A shape tensor used as the second input to IShuffleLayer can contain a -1 wildcard. + //! The corresponding minVal[i] should be -1. + //! + //! * A shape tensor used as the stride input to ISliceLayer can contain any valid strides. + //! The values could be positive, negative, or zero. + //! + //! * A shape tensor subtracted from zero to compute the size input of an ISliceLayer can + //! contain any non-positive values that yield a valid slice operation. + //! + //! Tightening the minVals and maxVals bounds to cover only values that are necessary may help optimization. //! //! \param inputName The input tensor name //! \param select Whether to set the minimum, optimum, or maximum input values. @@ -1091,9 +1112,11 @@ public: //! //! \warning If run on DLA, minimum, optimum, and maximum shape values must to be the same. //! - virtual bool setShapeValues( + bool setShapeValues( const char* inputName, OptProfileSelector select, const int32_t* values, int32_t nbValues) noexcept - = 0; + { + return mImpl->setShapeValues(inputName, select, values, nbValues); + } //! //! \brief Get the number of values for an input shape tensor. @@ -1101,14 +1124,20 @@ public: //! This will return the number of shape values if setShapeValues() has been called before for this input tensor. //! Otherwise, return -1. //! - virtual int32_t getNbShapeValues(const char* inputName) const noexcept = 0; + int32_t getNbShapeValues(const char* inputName) const noexcept + { + return mImpl->getNbShapeValues(inputName); + } //! //! \brief Get the minimum / optimum / maximum values for an input shape tensor. //! //! If the shape values have not been set previously with setShapeValues(), this returns nullptr. //! - virtual const int32_t* getShapeValues(const char* inputName, OptProfileSelector select) const noexcept = 0; + int32_t const* getShapeValues(const char* inputName, OptProfileSelector select) const noexcept + { + return mImpl->getShapeValues(inputName, select); + } //! //! \brief Set a target for extra GPU memory that may be used by this profile. @@ -1123,12 +1152,18 @@ public: //! //! \return true if the input is in the valid range (between 0 and 1 inclusive), else false //! - virtual bool setExtraMemoryTarget(float target) noexcept = 0; + bool setExtraMemoryTarget(float target) noexcept + { + return mImpl->setExtraMemoryTarget(target); + } //! //! \brief Get the extra memory target that has been defined for this profile. //! - virtual float getExtraMemoryTarget() const noexcept = 0; + float getExtraMemoryTarget() const noexcept + { + return mImpl->getExtraMemoryTarget(); + } //! //! \brief Check whether the optimization profile can be passed to an IBuilderConfig object. @@ -1141,12 +1176,45 @@ public: //! //! \return true if the optimization profile is valid and may be passed to an IBuilderConfig, else false //! - virtual bool isValid() const noexcept = 0; + bool isValid() const noexcept + { + return mImpl->isValid(); + } protected: - ~IOptimizationProfile() noexcept = default; + apiv::VOptimizationProfile* mImpl; + virtual ~IOptimizationProfile() noexcept = default; }; +//! +//! \enum TacticSource +//! +//! \brief List of tactic sources for TensorRT. +//! +//! \see TacticSources, IBuilderConfig::setTacticSources(), IBuilderConfig::getTacticSources() +//! +enum class TacticSource : int32_t +{ + //! \note Disabling kCUBLAS will cause the cublas handle passed to plugins in attachToContext to be null. + kCUBLAS = 0, //!< cuBLAS tactics. + kCUBLAS_LT = 1, //!< cuBLAS LT tactics + kCUDNN = 2 //!< cuDNN tactics +}; + +template <> +constexpr inline int32_t EnumMax() noexcept +{ + return 3; +} //!< Maximum number of tactic sources in TacticSource enum. \see TacticSource + +//! +//! \brief Represents a collection of one or more TacticSource values +//! combine using bitwise-OR operations. +//! +//! \see IBuilderConfig::setTacticSources(), IBuilderConfig::getTacticSources() +//! +using TacticSources = uint32_t; + //! //! \class ICudaEngine //! @@ -1154,9 +1222,11 @@ protected: //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. //! -class ICudaEngine +class ICudaEngine : public INoCopy { public: + virtual ~ICudaEngine() noexcept = default; + //! //! \brief Get the number of binding indices. //! @@ -1167,7 +1237,10 @@ public: //! //! \see getBindingIndex(); //! - virtual int32_t getNbBindings() const noexcept = 0; + int32_t getNbBindings() const noexcept + { + return mImpl->getNbBindings(); + } //! //! \brief Retrieve the binding index for a named tensor. @@ -1186,7 +1259,10 @@ public: //! //! \see getNbBindings() getBindingName() //! - virtual int32_t getBindingIndex(const char* name) const noexcept = 0; + int32_t getBindingIndex(const char* name) const noexcept + { + return mImpl->getBindingIndex(name); + } //! //! \brief Retrieve the name corresponding to a binding index. @@ -1203,7 +1279,10 @@ public: //! //! \see getBindingIndex() //! - virtual const char* getBindingName(int32_t bindingIndex) const noexcept = 0; + const char* getBindingName(int32_t bindingIndex) const noexcept + { + return mImpl->getBindingName(bindingIndex); + } //! //! \brief Determine whether a binding is an input binding. @@ -1213,7 +1292,10 @@ public: //! //! \see getBindingIndex() //! - virtual bool bindingIsInput(int32_t bindingIndex) const noexcept = 0; + bool bindingIsInput(int32_t bindingIndex) const noexcept + { + return mImpl->bindingIsInput(bindingIndex); + } //! //! \brief Get the dimensions of a binding. @@ -1235,7 +1317,10 @@ public: //! //! \see getBindingIndex() //! - virtual Dims getBindingDimensions(int32_t bindingIndex) const noexcept = 0; + Dims getBindingDimensions(int32_t bindingIndex) const noexcept + { + return mImpl->getBindingDimensions(bindingIndex); + } //! //! \brief Determine the required data type for a buffer from its binding index. @@ -1245,7 +1330,10 @@ public: //! //! \see getBindingIndex() //! - virtual DataType getBindingDataType(int32_t bindingIndex) const noexcept = 0; + DataType getBindingDataType(int32_t bindingIndex) const noexcept + { + return mImpl->getBindingDataType(bindingIndex); + } //! //! \brief Get the maximum batch size which can be used for inference. @@ -1254,7 +1342,10 @@ public: //! //! \return The maximum batch size for this engine. //! - virtual int32_t getMaxBatchSize() const noexcept = 0; + int32_t getMaxBatchSize() const noexcept + { + return mImpl->getMaxBatchSize(); + } //! //! \brief Get the number of layers in the network. @@ -1265,32 +1356,24 @@ public: //! //! \return The number of layers in the network. //! - virtual int32_t getNbLayers() const noexcept = 0; - - //! - //! \brief Get the amount of workspace the engine uses. - //! - //! The workspace size will be no greater than the value provided to the builder when the engine was built, and will - //! typically be smaller. Workspace will be allocated for each execution context. - //! - //! This method is not used because getDeviceMemorySize returns the total amount of device memory required by an - //! execution context. - //! - //! \deprecated Deprecated interface will be removed in TensorRT 8.0. - //! - TRT_DEPRECATED - virtual std::size_t getWorkspaceSize() const noexcept = 0; + int32_t getNbLayers() const noexcept + { + return mImpl->getNbLayers(); + } //! //! \brief Serialize the network to a stream. //! //! \return A IHostMemory object that contains the serialized engine. //! - //! The network may be deserialized with IRuntime::deserializeCudaEngine() and also safe::IRuntime::deserializeCudaEngine() if only functional-safe features are used in the engine. + //! The network may be deserialized with IRuntime::deserializeCudaEngine(). //! - //! \see IRuntime::deserializeCudaEngine() safe::IRuntime::deserializeCudaEngine() + //! \see IRuntime::deserializeCudaEngine() //! - virtual IHostMemory* serialize() const noexcept = 0; + IHostMemory* serialize() const noexcept + { + return mImpl->serialize(); + } //! //! \brief Create an execution context. @@ -1298,16 +1381,27 @@ public: //! If the engine supports dynamic shapes, each execution context in concurrent use must use a separate optimization //! profile. The first execution context created will call setOptimizationProfile(0) implicitly. For other execution //! contexts, setOptimizationProfile() must be called with unique profile index before calling execute or enqueue. + //! If an error recorder has been set for the engine, it will also be passed to the execution context. //! //! \see IExecutionContext. //! \see IExecutionContext::setOptimizationProfile() //! - virtual IExecutionContext* createExecutionContext() noexcept = 0; + IExecutionContext* createExecutionContext() noexcept + { + return mImpl->createExecutionContext(); + } //! //! \brief Destroy this object; //! - virtual void destroy() noexcept = 0; + //! \deprecated Deprecated interface will be removed in TensorRT 10.0. + //! + //! \warning Calling destroy on a managed pointer will result in a double-free error. + //! + TRT_DEPRECATED void destroy() noexcept + { + delete this; + } //! //! \brief Get location of binding @@ -1319,33 +1413,39 @@ public: //! \param bindingIndex The binding index. //! \return The location of the bound tensor with given index. //! - virtual TensorLocation getLocation(int32_t bindingIndex) const noexcept = 0; + TensorLocation getLocation(int32_t bindingIndex) const noexcept + { + return mImpl->getLocation(bindingIndex); + } -protected: - virtual ~ICudaEngine() {} - -public: //! \brief create an execution context without any device memory allocated //! //! The memory for execution of this device context must be supplied by the application. //! - //! \see getDeviceMemorySize() IExecutionContext::setDeviceMemory() - //! - virtual IExecutionContext* createExecutionContextWithoutDeviceMemory() noexcept = 0; + IExecutionContext* createExecutionContextWithoutDeviceMemory() noexcept + { + return mImpl->createExecutionContextWithoutDeviceMemory(); + } //! //! \brief Return the amount of device memory required by an execution context. //! //! \see IExecutionContext::setDeviceMemory() //! - virtual size_t getDeviceMemorySize() const noexcept = 0; + size_t getDeviceMemorySize() const noexcept + { + return mImpl->getDeviceMemorySize(); + } //! - //! \brief Return true if engine can be refit. + //! \brief Return true if an engine can be refit. //! //! \see nvinfer1::createInferRefitter() //! - virtual bool isRefittable() const noexcept = 0; + bool isRefittable() const noexcept + { + return mImpl->isRefittable(); + } //! //! \brief Return the number of bytes per component of an element. @@ -1356,7 +1456,10 @@ public: //! //! \see ICudaEngine::getBindingVectorizedDim() //! - virtual int32_t getBindingBytesPerComponent(int32_t bindingIndex) const noexcept = 0; + int32_t getBindingBytesPerComponent(int32_t bindingIndex) const noexcept + { + return mImpl->getBindingBytesPerComponent(bindingIndex); + } //! //! \brief Return the number of components included in one element. @@ -1367,14 +1470,20 @@ public: //! //! \see ICudaEngine::getBindingVectorizedDim() //! - virtual int32_t getBindingComponentsPerElement(int32_t bindingIndex) const noexcept = 0; + int32_t getBindingComponentsPerElement(int32_t bindingIndex) const noexcept + { + return mImpl->getBindingComponentsPerElement(bindingIndex); + } //! //! \brief Return the binding format. //! //! \param bindingIndex The binding Index. //! - virtual TensorFormat getBindingFormat(int32_t bindingIndex) const noexcept = 0; + TensorFormat getBindingFormat(int32_t bindingIndex) const noexcept + { + return mImpl->getBindingFormat(bindingIndex); + } //! //! \brief Return the human readable description of the tensor format. @@ -1390,7 +1499,10 @@ public: //! //! \param bindingIndex The binding Index. //! - virtual const char* getBindingFormatDesc(int32_t bindingIndex) const noexcept = 0; + const char* getBindingFormatDesc(int32_t bindingIndex) const noexcept + { + return mImpl->getBindingFormatDesc(bindingIndex); + } //! //! \brief Return the dimension index that the buffer is vectorized. @@ -1399,7 +1511,10 @@ public: //! //! \param bindingIndex The binding Index. //! - virtual int32_t getBindingVectorizedDim(int32_t bindingIndex) const noexcept = 0; + int32_t getBindingVectorizedDim(int32_t bindingIndex) const noexcept + { + return mImpl->getBindingVectorizedDim(bindingIndex); + } //! //! \brief Returns the name of the network associated with the engine. @@ -1411,7 +1526,10 @@ public: //! //! \return A zero delimited C-style string representing the name of the network. //! - virtual const char* getName() const noexcept = 0; + const char* getName() const noexcept + { + return mImpl->getName(); + } //! //! \brief Get the number of optimization profiles defined for this engine. @@ -1419,7 +1537,10 @@ public: //! \return Number of optimization profiles. It is always at least 1. //! //! \see IExecutionContext::setOptimizationProfile() - virtual int32_t getNbOptimizationProfiles() const noexcept = 0; + int32_t getNbOptimizationProfiles() const noexcept + { + return mImpl->getNbOptimizationProfiles(); + } //! //! \brief Get the minimum / optimum / maximum dimensions for a particular binding under an optimization profile. @@ -1443,9 +1564,10 @@ public: //! //! Otherwise the bindingIndex is considered invalid. //! - virtual Dims getProfileDimensions(int32_t bindingIndex, int32_t profileIndex, OptProfileSelector select) const - noexcept - = 0; + Dims getProfileDimensions(int32_t bindingIndex, int32_t profileIndex, OptProfileSelector select) const noexcept + { + return mImpl->getProfileDimensions(bindingIndex, profileIndex, select); + } //! //! \brief Get minimum / optimum / maximum values for an input shape binding under an optimization profile. @@ -1468,9 +1590,11 @@ public: //! //! \see ICudaEngine::getProfileDimensions //! - virtual const int32_t* getProfileShapeValues( - int32_t profileIndex, int32_t inputIndex, OptProfileSelector select) const noexcept - = 0; + const int32_t* getProfileShapeValues(int32_t profileIndex, int32_t inputIndex, OptProfileSelector select) const + noexcept + { + return mImpl->getProfileShapeValues(profileIndex, inputIndex, select); + } //! //! \brief True if tensor is required as input for shape calculations or output from them. @@ -1503,7 +1627,10 @@ public: //! //! \see isExecutionBinding() //! - virtual bool isShapeBinding(int32_t bindingIndex) const noexcept = 0; + bool isShapeBinding(int32_t bindingIndex) const noexcept + { + return mImpl->isShapeBinding(bindingIndex); + } //! //! \brief True if pointer to tensor data is required for execution phase, false if nullptr can be supplied. @@ -1514,19 +1641,25 @@ public: //! //! \see isShapeBinding() //! - virtual bool isExecutionBinding(int32_t bindingIndex) const noexcept = 0; + bool isExecutionBinding(int32_t bindingIndex) const noexcept + { + return mImpl->isExecutionBinding(bindingIndex); + } //! - //! \brief determine that execution capability this engine has. + //! \brief Determine what execution capability this engine has. //! - //! If the engine has EngineCapability::kDEFAULT, then all engine functionality is valid.. - //! If the engine has EngineCapability::kSAFE_GPU, then only the functionality in safe::ICudaEngine is valid. - //! If the engine has EngineCapability::kSAFE_DLA, then only serialize, destroy, and const-accessor functions are + //! If the engine has EngineCapability::kSTANDARD, then all engine functionality is valid. + //! If the engine has EngineCapability::kSAFETY, then only the functionality in safe engine is valid. + //! If the engine has EngineCapability::kDLA_STANDALONE, then only serialize, destroy, and const-accessor functions are //! valid. //! //! \return The EngineCapability flag that the engine was built for. //! - virtual EngineCapability getEngineCapability() const noexcept = 0; + EngineCapability getEngineCapability() const noexcept + { + return mImpl->getEngineCapability(); + } //! \brief Set the ErrorRecorder for this interface //! @@ -1535,23 +1668,31 @@ public: //! recorder to nullptr unregisters the recorder with the interface, resulting in a call to decRefCount if //! a recorder has been registered. //! + //! If an error recorder is not set, messages will be sent to the global log stream. + //! //! \param recorder The error recorder to register with this interface. // - //! \see getErrorRecorder + //! \see getErrorRecorder() //! - virtual void setErrorRecorder(IErrorRecorder* recorder) noexcept = 0; + void setErrorRecorder(IErrorRecorder* recorder) noexcept + { + return mImpl->setErrorRecorder(recorder); + } //! - //! \brief get the ErrorRecorder assigned to this interface. + //! \brief Get the ErrorRecorder assigned to this interface. //! - //! Retrieves the assigned error recorder object for the given class. A default error recorder does not exist, - //! so a nullptr will be returned if setErrorRecorder has not been called. + //! Retrieves the assigned error recorder object for the given class. A nullptr will be returned if + //! an error handler has not been set. //! //! \return A pointer to the IErrorRecorder object that has been registered. //! - //! \see setErrorRecorder + //! \see setErrorRecorder() //! - virtual IErrorRecorder* getErrorRecorder() const noexcept = 0; + IErrorRecorder* getErrorRecorder() const noexcept + { + return mImpl->getErrorRecorder(); + } //! //! \brief Query whether the engine was built with an implicit batch dimension. @@ -1567,7 +1708,22 @@ public: //! //! \see createNetworkV2 //! - virtual bool hasImplicitBatchDimension() const TRTNOEXCEPT = 0; + bool hasImplicitBatchDimension() const noexcept + { + return mImpl->hasImplicitBatchDimension(); + } + + //! \brief return the tactic sources required by this engine + //! + //! \see IBuilderConfig::setTacticSources() + //! + TacticSources getTacticSources() const noexcept + { + return mImpl->getTacticSources(); + } + +protected: + apiv::VCudaEngine* mImpl; }; //! @@ -1580,38 +1736,61 @@ public: //! dynamic shapes, each execution context in concurrent use must use a separate optimization profile. //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. -class IExecutionContext +class IExecutionContext : public INoCopy { public: + virtual ~IExecutionContext() noexcept = default; + //! //! \brief Synchronously execute inference on a batch. //! - //! This method requires an array of input and output buffers. The mapping from tensor names to indices can be - //! queried using ICudaEngine::getBindingIndex() \param batchSize The batch size. This is at most the value supplied - //! when the engine was built. \param bindings An array of pointers to input and output buffers for the network. + //! This method requires an array of input and output buffers. The mapping from tensor names to indices + //! can be queried using ICudaEngine::getBindingIndex() + //! + //! \param batchSize The batch size. This is at most the value supplied when the engine was built. + //! \param bindings An array of pointers to input and output buffers for the network. //! //! \return True if execution succeeded. //! + //! \warning This function will trigger layer resource updates if hasImplicitBatchDimension() + //! returns true and batchSize changes between subsequent calls, possibly resulting + //! in performance bottlenecks. + //! //! \see ICudaEngine::getBindingIndex() ICudaEngine::getMaxBatchSize() //! - virtual bool execute(int32_t batchSize, void** bindings) noexcept = 0; + bool execute(int32_t batchSize, void* const* bindings) noexcept + { + return mImpl->execute(batchSize, bindings); + } //! //! \brief Asynchronously execute inference on a batch. //! //! This method requires an array of input and output buffers. The mapping from tensor names to indices can be //! queried using ICudaEngine::getBindingIndex() \param batchSize The batch size. This is at most the value supplied - //! when the engine was built. \param bindings An array of pointers to input and output buffers for the network. - //! \param stream A cuda stream on which the inference kernels will be enqueued + //! when the engine was built. + //! + //! \param bindings An array of pointers to input and output buffers for the network. + //! \param stream A cuda stream on which the inference kernels will be enqueued. //! \param inputConsumed An optional event which will be signaled when the input buffers can be refilled with new - //! data + //! data. //! //! \return True if the kernels were enqueued successfully. //! //! \see ICudaEngine::getBindingIndex() ICudaEngine::getMaxBatchSize() //! - virtual bool enqueue(int32_t batchSize, void** bindings, cudaStream_t stream, cudaEvent_t* inputConsumed) noexcept - = 0; + //! \warning Calling enqueue() in from the same IExecutionContext object with different CUDA streams concurrently + //! results in undefined behavior. To perform inference concurrently in multiple streams, use one execution + //! context per stream. + //! + //! \warning This function will trigger layer resource updates if hasImplicitBatchDimension() + //! returns true and batchSize changes between subsequent calls, possibly resulting in performance + //! bottlenecks. + //! + bool enqueue(int32_t batchSize, void* const* bindings, cudaStream_t stream, cudaEvent_t* inputConsumed) noexcept + { + return mImpl->enqueue(batchSize, bindings, stream, inputConsumed); + } //! //! \brief Set the debug sync flag. @@ -1621,45 +1800,63 @@ public: //! //! \see getDebugSync() //! - virtual void setDebugSync(bool sync) noexcept = 0; + void setDebugSync(bool sync) noexcept + { + mImpl->setDebugSync(sync); + } //! //! \brief Get the debug sync flag. //! //! \see setDebugSync() //! - virtual bool getDebugSync() const noexcept = 0; + bool getDebugSync() const noexcept + { + return mImpl->getDebugSync(); + } //! //! \brief Set the profiler. //! //! \see IProfiler getProfiler() //! - virtual void setProfiler(IProfiler*) noexcept = 0; + void setProfiler(IProfiler* profiler) noexcept + { + mImpl->setProfiler(profiler); + } //! //! \brief Get the profiler. //! //! \see IProfiler setProfiler() //! - virtual IProfiler* getProfiler() const noexcept = 0; + IProfiler* getProfiler() const noexcept + { + return mImpl->getProfiler(); + } //! //! \brief Get the associated engine. //! //! \see ICudaEngine //! - virtual const ICudaEngine& getEngine() const noexcept = 0; + const ICudaEngine& getEngine() const noexcept + { + return mImpl->getEngine(); + } //! //! \brief Destroy this object. //! - virtual void destroy() noexcept = 0; + //! \deprecated Deprecated interface will be removed in TensorRT 10.0. + //! + //! \warning Calling destroy on a managed pointer will result in a double-free error. + //! + TRT_DEPRECATED void destroy() noexcept + { + delete this; + } -protected: - virtual ~IExecutionContext() noexcept {} - -public: //! //! \brief Set the name of the execution context. //! @@ -1667,14 +1864,20 @@ public: //! //! \see getName() //! - virtual void setName(const char* name) noexcept = 0; + void setName(const char* name) noexcept + { + mImpl->setName(name); + } //! //! \brief Return the name of the execution context. //! //! \see setName() //! - virtual const char* getName() const noexcept = 0; + const char* getName() const noexcept + { + return mImpl->getName(); + } //! //! \brief Set the device memory for use by this execution context. @@ -1687,7 +1890,12 @@ public: //! //! \see ICudaEngine::getDeviceMemorySize() ICudaEngine::createExecutionContextWithoutDeviceMemory() //! - virtual void setDeviceMemory(void* memory) noexcept = 0; + + TRT_DEPRECATED + void setDeviceMemory(void* memory) noexcept + { + mImpl->setDeviceMemory(memory); + } //! //! \brief Return the strides of the buffer for the given binding. @@ -1705,7 +1913,10 @@ public: //! //! \see getBindingComponentsPerElement //! - virtual Dims getStrides(int32_t bindingIndex) const noexcept = 0; + Dims getStrides(int32_t bindingIndex) const noexcept + { + return mImpl->getStrides(bindingIndex); + } public: //! @@ -1734,6 +1945,9 @@ public: //! setInputShapeBinding() for all dynamic input tensors or input shape tensors, which in //! turn must be called before either execute() or enqueue(). //! + //! \warning This function will trigger layer resource updates on the next + //! call of enqueue[V2]()/execute[V2](), possibly resulting in performance bottlenecks. + //! //! \return true if the call succeeded, else false (e.g. input out of range) //! //! \deprecated This API is superseded by setOptimizationProfileAsync and will be removed in TensorRT 9.0. @@ -1741,7 +1955,10 @@ public: //! \see ICudaEngine::getNbOptimizationProfiles() IExecutionContext::setOptimizationProfileAsync() //! TRT_DEPRECATED - virtual bool setOptimizationProfile(int32_t profileIndex) noexcept = 0; + bool setOptimizationProfile(int32_t profileIndex) noexcept + { + return mImpl->setOptimizationProfile(profileIndex); + } //! //! \brief Get the index of the currently selected optimization profile. @@ -1750,18 +1967,23 @@ public: //! to be created, or explicitly for all subsequent contexts), an invalid value of -1 will be returned //! and all calls to enqueue() or execute() will fail until a valid profile index has been set. //! - virtual int32_t getOptimizationProfile() const noexcept = 0; + int32_t getOptimizationProfile() const noexcept + { + return mImpl->getOptimizationProfile(); + } //! //! \brief Set the dynamic dimensions of a binding //! - //! Requires the engine to be built without an implicit batch dimension. - //! The binding must be an input tensor, and all dimensions must be compatible with - //! the network definition (i.e. only the wildcard dimension -1 can be replaced with a - //! new dimension > 0). Furthermore, the dimensions must be in the valid range for the - //! currently selected optimization profile, and the corresponding engine must not be - //! safety-certified. + //! \param bindingIndex index of an input tensor whose dimensions must be compatible with + //! the network definition (i.e. only the wildcard dimension -1 can be replaced with a + //! new dimension >= 0). //! + //! \param dimensions specifies the dimensions of the input tensor. It must be in the valid + //! range for the currently selected optimization profile, and the corresponding engine must + //! not be safety-certified. + //! + //! This method requires the engine to be built without an implicit batch dimension. //! This method will fail unless a valid optimization profile is defined for the current //! execution context (getOptimizationProfile() must not be -1). //! @@ -1769,11 +1991,23 @@ public: //! this method needs to be called before either enqueue() or execute() may be called. //! This can be checked using the method allInputDimensionsSpecified(). //! - //! \return false if an error occurs (e.g. index out of range), else true + //! \warning This function will trigger layer resource updates on the next + //! call of enqueue[V2]()/execute[V2](), possibly resulting in performance bottlenecks, + //! if the dimensions are different than the previous set dimensions. + //! + //! \return false if an error occurs (e.g. bindingIndex is out of range for the currently selected + //! optimization profile or binding dimension is inconsistent with min-max range of the + //! optimization profile), else true. Note that the network can still be invalid for certain + //! combinations of input shapes that lead to invalid output shapes. To confirm the correctness + //! of the network input shapes, check whether the output binding has valid + //! dimensions using getBindingDimensions() on the output bindingIndex. //! //! \see ICudaEngine::getBindingIndex //! - virtual bool setBindingDimensions(int32_t bindingIndex, Dims dimensions) noexcept = 0; + bool setBindingDimensions(int32_t bindingIndex, Dims dimensions) noexcept + { + return mImpl->setBindingDimensions(bindingIndex, dimensions); + } //! //! \brief Get the dynamic dimensions of a binding @@ -1800,7 +2034,10 @@ public: //! //! \see ICudaEngine::getProfileDimensions //! - virtual Dims getBindingDimensions(int32_t bindingIndex) const noexcept = 0; + Dims getBindingDimensions(int32_t bindingIndex) const noexcept + { + return mImpl->getBindingDimensions(bindingIndex); + } //! //! \brief Set values of input tensor required by shape calculations. @@ -1817,7 +2054,20 @@ public: //! This method will fail unless a valid optimization profile is defined for the current //! execution context (getOptimizationProfile() must not be -1). //! - virtual bool setInputShapeBinding(int32_t bindingIndex, const int32_t* data) noexcept = 0; + //! \warning This function will trigger layer resource updates on the next call of + //! enqueue[V2]()/execute[V2](), possibly resulting in performance bottlenecks, if the + //! shapes are different than the previous set shapes. + //! + //! \return false if an error occurs (e.g. bindingIndex is out of range for the currently selected + //! optimization profile or shape data is inconsistent with min-max range of the + //! optimization profile), else true. Note that the network can still be invalid for certain + //! combinations of input shapes that lead to invalid output shapes. To confirm the correctness + //! of the network input shapes, check whether the output binding has valid + //! dimensions using getBindingDimensions() on the output bindingIndex. + bool setInputShapeBinding(int32_t bindingIndex, int32_t const* data) noexcept + { + return mImpl->setInputShapeBinding(bindingIndex, data); + } //! //! \brief Get values of an input tensor required for shape calculations or an output tensor produced by shape @@ -1836,7 +2086,10 @@ public: //! //! \see isShapeBinding(bindingIndex) //! - virtual bool getShapeBinding(int32_t bindingIndex, int32_t* data) const noexcept = 0; + bool getShapeBinding(int32_t bindingIndex, int32_t* data) const noexcept + { + return mImpl->getShapeBinding(bindingIndex, data); + } //! //! \brief Whether all dynamic dimensions of input tensors have been specified @@ -1848,7 +2101,10 @@ public: //! //! \see setBindingDimensions(bindingIndex,dimensions) //! - virtual bool allInputDimensionsSpecified() const noexcept = 0; + bool allInputDimensionsSpecified() const noexcept + { + return mImpl->allInputDimensionsSpecified(); + } //! //! \brief Whether all input shape bindings have been specified @@ -1859,7 +2115,11 @@ public: //! //! \see isShapeBinding(bindingIndex) //! - virtual bool allInputShapesSpecified() const noexcept = 0; + bool allInputShapesSpecified() const noexcept + + { + return mImpl->allInputShapesSpecified(); + } //! //! \brief Set the ErrorRecorder for this interface @@ -1869,23 +2129,31 @@ public: //! recorder to nullptr unregisters the recorder with the interface, resulting in a call to decRefCount if //! a recorder has been registered. //! + //! If an error recorder is not set, messages will be sent to the global log stream. + //! //! \param recorder The error recorder to register with this interface. // - //! \see getErrorRecorder + //! \see getErrorRecorder() //! - virtual void setErrorRecorder(IErrorRecorder* recorder) noexcept = 0; + void setErrorRecorder(IErrorRecorder* recorder) noexcept + { + mImpl->setErrorRecorder(recorder); + } //! - //! \brief get the ErrorRecorder assigned to this interface. + //! \brief Get the ErrorRecorder assigned to this interface. //! - //! Retrieves the assigned error recorder object for the given class. A default error recorder does not exist, - //! so a nullptr will be returned if setErrorRecorder has not been called. + //! Retrieves the assigned error recorder object for the given class. A nullptr will be returned if + //! an error handler has not been set. //! //! \return A pointer to the IErrorRecorder object that has been registered. //! - //! \see setErrorRecorder + //! \see setErrorRecorder() //! - virtual IErrorRecorder* getErrorRecorder() const noexcept = 0; + IErrorRecorder* getErrorRecorder() const noexcept + { + return mImpl->getErrorRecorder(); + } //! //! \brief Synchronously execute inference a network. @@ -1899,7 +2167,10 @@ public: //! //! \see ICudaEngine::getBindingIndex() ICudaEngine::getMaxBatchSize() //! - virtual bool executeV2(void** bindings) noexcept = 0; + bool executeV2(void* const* bindings) noexcept + { + return mImpl->executeV2(bindings); + } //! //! \brief Asynchronously execute inference. @@ -1920,13 +2191,20 @@ public: //! used, the first enqueueV2() call after a setInputShapeBinding() call will cause failure in stream capture //! due to resource allocation. Please call enqueueV2() once before capturing the graph. //! - virtual bool enqueueV2(void** bindings, cudaStream_t stream, cudaEvent_t* inputConsumed) noexcept = 0; + //! \warning Calling enqueueV2() in from the same IExecutionContext object with different CUDA streams concurrently + //! results in undefined behavior. To perform inference concurrently in multiple streams, use one execution + //! context per stream. + //! + bool enqueueV2(void* const* bindings, cudaStream_t stream, cudaEvent_t* inputConsumed) noexcept + { + return mImpl->enqueueV2(bindings, stream, inputConsumed); + } //! //! \brief Select an optimization profile for the current context with async //! semantics. //! - //! \param profileIndex Index of the profile. It must lie between 0 and + //! \param profileIndex Index of the profile. The value must lie between 0 and //! getEngine().getNbOptimizationProfiles() - 1 //! //! \param stream A cuda stream on which the cudaMemcpyAsyncs may be @@ -1954,6 +2232,9 @@ public: //! tensors or input shape tensors, which in turn must be called before //! either execute() or enqueue(). //! + //! \warning This function will trigger layer resource updates on the next call of + //! enqueue[V2]()/execute[V2](), possibly resulting in performance bottlenecks. + //! //! \warning Not synchronizing the stream used at enqueue with the stream //! used to set optimization profile asynchronously using this API will //! result in undefined behavior. @@ -1961,8 +2242,14 @@ public: //! \return true if the call succeeded, else false (e.g. input out of range) //! //! \see ICudaEngine::getNbOptimizationProfiles() - //! IExecutionContext::setOptimizationProfile() - virtual bool setOptimizationProfileAsync(int32_t profileIndex, cudaStream_t stream) noexcept = 0; + //! \see IExecutionContext::setOptimizationProfile() + bool setOptimizationProfileAsync(int32_t profileIndex, cudaStream_t stream) noexcept + { + return mImpl->setOptimizationProfileAsync(profileIndex, stream); + } + +protected: + apiv::VExecutionContext* mImpl; }; // class IExecutionContext } // namespace nvinfer1 @@ -1970,24 +2257,35 @@ public: //! Internal C entry point for creating IRuntime. //! @private //! -extern "C" TENSORRTAPI void* createInferRuntime_INTERNAL(void* logger, int32_t version); +extern "C" TENSORRTAPI void* createInferRuntime_INTERNAL(void* logger, int32_t version) noexcept; //! //! Internal C entry point for creating IRefitter. //! @private //! -extern "C" TENSORRTAPI void* createInferRefitter_INTERNAL(void* engine, void* logger, int32_t version); +extern "C" TENSORRTAPI void* createInferRefitter_INTERNAL(void* engine, void* logger, int32_t version) noexcept; + +//! +//! \brief Return the plugin registry +//! +extern "C" TENSORRTAPI nvinfer1::IPluginRegistry* getPluginRegistry() noexcept; + +//! +//! \brief Return the logger object. +//! +extern "C" TENSORRTAPI nvinfer1::ILogger* getLogger() noexcept; namespace nvinfer1 { -namespace // unnamed namespace avoids linkage surprises when linking objects built with different versions of this header. +namespace // unnamed namespace avoids linkage surprises when linking objects built with different versions of this + // header. { //! //! \brief Create an instance of an IRuntime class. //! //! This class is the logging class for the runtime. //! -inline IRuntime* createInferRuntime(ILogger& logger) +inline IRuntime* createInferRuntime(ILogger& logger) noexcept { return static_cast(createInferRuntime_INTERNAL(&logger, NV_TENSORRT_VERSION)); } @@ -1995,13 +2293,42 @@ inline IRuntime* createInferRuntime(ILogger& logger) //! //! \brief Create an instance of an IRefitter class. //! -//! This class is the logging class for the refitter. +//! This is the logging class for the refitter. //! -inline IRefitter* createInferRefitter(ICudaEngine& engine, ILogger& logger) +inline IRefitter* createInferRefitter(ICudaEngine& engine, ILogger& logger) noexcept { return static_cast(createInferRefitter_INTERNAL(&engine, &logger, NV_TENSORRT_VERSION)); } -} -} +} // namespace + +//! +//! \brief Register the plugin creator to the registry +//! The static registry object will be instantiated when the plugin library is +//! loaded. This static object will register all creators available in the +//! library to the registry. +//! +//! \warning Statically registering plugins should be avoided in the automotive +//! safety context as the application developer should first register an error recorder +//! with the plugin registry via IPluginRegistry::setErrorRecorder() before using +//! IPluginRegistry::registerCreator() or other methods. +//! +template +class PluginRegistrar +{ +public: + PluginRegistrar() + { + getPluginRegistry()->registerCreator(instance, ""); + } + +private: + //! Plugin instance. + T instance{}; +}; + +} // namespace nvinfer1 + +#define REGISTER_TENSORRT_PLUGIN(name) \ + static nvinfer1::PluginRegistrar pluginRegistrar##name {} #endif // NV_INFER_RUNTIME_H diff --git a/include/NvInferRuntimeCommon.h b/include/NvInferRuntimeCommon.h index dd681cf2..ef60bcbc 100644 --- a/include/NvInferRuntimeCommon.h +++ b/include/NvInferRuntimeCommon.h @@ -17,17 +17,9 @@ #ifndef NV_INFER_RUNTIME_COMMON_H #define NV_INFER_RUNTIME_COMMON_H +#include "NvInferVersion.h" #include #include -#include "NvInferVersion.h" - -#if __cplusplus >= 201103L -#define _TENSORRT_FINAL final -#define _TENSORRT_OVERRIDE override -#else -#define _TENSORRT_FINAL -#define _TENSORRT_OVERRIDE -#endif //!< Items that are marked as deprecated will be removed in a future release. #if __cplusplus >= 201402L @@ -64,9 +56,6 @@ #else #define TENSORRTAPI #endif - -//! Defined for use with legacy APIs that have not been updated to noexcept yet. -//! Do not use with new APIs, use noexcept instead. #define TRTNOEXCEPT //! //! \file NvInferRuntimeCommon.h @@ -76,15 +65,21 @@ // forward declare some CUDA types to avoid an include dependency -struct cublasContext; -struct cudnnContext; +extern "C" +{ + //! Forward declaration of cublasContext to use in other interfaces + struct cublasContext; + //! Forward declaration of cudnnContext to use in other interfaces + struct cudnnContext; -typedef struct CUstream_st* cudaStream_t; //!< Forward declaration of cudaStream_t. -typedef struct CUevent_st* cudaEvent_t; //!< Forward declaration of cudaEvent_t. + //! Forward declaration of cudaStream_t. + using cudaStream_t = struct CUstream_st*; -static const int32_t NV_TENSORRT_VERSION - = (NV_TENSORRT_MAJOR * 1000) + (NV_TENSORRT_MINOR * 100) + NV_TENSORRT_PATCH; // major, minor, patch + //! Forward declaration of cudaEvent_t. + using cudaEvent_t = struct CUevent_st*; +} +#define NV_TENSORRT_VERSION nvinfer1::kNV_TENSORRT_VERSION_IMPL //! //! \namespace nvinfer1 //! @@ -93,44 +88,35 @@ static const int32_t NV_TENSORRT_VERSION namespace nvinfer1 { -class IErrorRecorder; //!< Forward declare IErrorRecorder for use in other interfaces. -class IGpuAllocator; //!< Forward declare IGpuAllocator for use in other interfaces. +static constexpr int32_t kNV_TENSORRT_VERSION_IMPL + = (NV_TENSORRT_MAJOR * 1000) + (NV_TENSORRT_MINOR * 100) + NV_TENSORRT_PATCH; // major, minor, patch + +//! char_t is the type used by TensorRT to represent all valid characters. +using char_t = char; +//! AsciiChar is the type used by TensorRT to represent valid ASCII characters. +using AsciiChar = char_t; + +//! Forward declare IErrorRecorder for use in other interfaces. +class IErrorRecorder; +//! Forward declare IGpuAllocator for use in other interfaces. +class IGpuAllocator; + +namespace impl +{ +//! Declaration of EnumMaxImpl struct to store maximum number of elements in an enumeration type. +template +struct EnumMaxImpl; +} // namespace impl //! Maximum number of elements in an enumeration type. template -constexpr inline int32_t EnumMax(); - -//! -//! \enum ActivationType -//! -//! \brief Enumerates the types of activation to perform in an activation layer. -//! -enum class ActivationType : int32_t +constexpr int32_t EnumMax() noexcept { - kRELU = 0, //!< Rectified linear activation. - kSIGMOID = 1, //!< Sigmoid activation. - kTANH = 2, //!< TanH activation. - kLEAKY_RELU = 3, //!< LeakyRelu activation: x>=0 ? x : alpha * x. - kELU = 4, //!< Elu activation: x>=0 ? x : alpha * (exp(x) - 1). - kSELU = 5, //!< Selu activation: x>0 ? beta * x : beta * (alpha*exp(x) - alpha) - kSOFTSIGN = 6, //!< Softsign activation: x / (1+|x|) - kSOFTPLUS = 7, //!< Parametric softplus activation: alpha*log(exp(beta*x)+1) - kCLIP = 8, //!< Clip activation: max(alpha, min(beta, x)) - kHARD_SIGMOID = 9, //!< Hard sigmoid activation: max(0, min(1, alpha*x+beta)) - kSCALED_TANH = 10, //!< Scaled tanh activation: alpha*tanh(beta*x) - kTHRESHOLDED_RELU = 11 //!< Thresholded ReLU activation: x>alpha ? x : 0 -}; - -//! Maximum number of elements in ActivationType enum. \see ActivationType -template <> -constexpr inline int32_t EnumMax() -{ - return 12; + return impl::EnumMaxImpl::kVALUE; } //! //! \enum DataType -//! //! \brief The type of weights and tensors. //! enum class DataType : int32_t @@ -151,63 +137,44 @@ enum class DataType : int32_t kBOOL = 4 }; +namespace impl +{ //! Maximum number of elements in DataType enum. \see DataType template <> -constexpr inline int32_t EnumMax() +struct EnumMaxImpl { - return 5; -} - -//! -//! \enum DimensionType -//! \brief The type of data encoded across this dimension. -//! -enum class DimensionType : int32_t -{ - kSPATIAL = 0, //!< Elements correspond to different spatial data. - kCHANNEL = 1, //!< Elements correspond to different channels. - kINDEX = 2, //!< Elements correspond to different batch index. - kSEQUENCE = 3 //!< Elements correspond to different sequence values. + // Declaration of kVALUE that represents maximum number of elements in DataType enum + static constexpr int32_t kVALUE = 5; }; - -//! Maximum number of elements in DimensionType enum. \see DimensionType -template <> -constexpr inline int32_t EnumMax() -{ - return 4; -} +} // namespace impl //! //! \class Dims //! \brief Structure to define the dimensions of a tensor. //! -//! \note: Currently the following formats are supported for layer inputs and outputs: -//! * zero or more index dimensions followed by one channel and two spatial dimensions (e.g. CHW) -//! * one time series dimension followed by one index dimension followed by one channel dimension (i.e. TNC) -//! //! TensorRT can also return an invalid dims structure. This structure is represented by nbDims == -1 //! and d[i] == 0 for all d. //! //! TensorRT can also return an "unknown rank" dims structure. This structure is represented by nbDims == -1 //! and d[i] == -1 for all d. //! -class Dims +class Dims32 { public: - static const int32_t MAX_DIMS = 8; //!< The maximum number of dimensions supported for a tensor. - int32_t nbDims; //!< The number of dimensions. - int32_t d[MAX_DIMS]; //!< The extent of each dimension. - TRT_DEPRECATED DimensionType type[MAX_DIMS]; //!< The type of each dimension, provided for backwards compatibility - //!< and will be removed in TensorRT 8.0. + //! The maximum number of dimensions supported for a tensor. + static constexpr int32_t MAX_DIMS{8}; + //! The number of dimensions. + int32_t nbDims; + //! The extent of each dimension. + int32_t d[MAX_DIMS]; }; //! -//! \brief It is capable of representing one or more TensorFormat by binary OR -//! operations, e.g., 1U << TensorFormats::kCHW4 | 1U << TensorFormats::kCHW32. +//! Alias for Dims32. //! -//! \see ITensor::getAllowedFormats(), ITensor::setAllowedFormats(), +//! \warning: This alias might change in the future. //! -typedef uint32_t TensorFormats; +using Dims = Dims32; //! //! \enum TensorFormat @@ -217,22 +184,21 @@ typedef uint32_t TensorFormats; //! This enum is extended to be used by both plugins and reformat-free network //! I/O tensors. //! -//! \see IPluginExt::getPluginFormats(), safe::ICudaEngine::getBindingFormat() +//! \see IPluginV2::supportsFormat(), safe::ICudaEngine::getBindingFormat() //! //! For more information about data formats, see the topic "Data Format Description" located in the -//! TensorRT Developer Guide (https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html). +//! TensorRT Developer Guide. //! enum class TensorFormat : int32_t { //! Row major linear format. - //! For a tensor with dimensions {N, C, H, W}, the W axis always has - //! unit stride, and the stride of every other axis is at least the the - //! product of of the next dimension times the next stride. the strides - //! are the same as for a C array with dimensions [N][C][H][W]. + //! For a tensor with dimensions {N, C, H, W} or {numbers, channels, + //! columns, rows}, the dimensional index corresponds to {3, 2, 1, 0} + //! and thus the order is W minor. + //! + //! For DLA usage, the tensor sizes are limited to C,H,W in the range [1,8192]. + //! kLINEAR = 0, - //! Deprecated name of kLINEAR, provided for backwards compatibility and will - //! be removed in TensorRT 8.0. - kNCHW TRT_DEPRECATED_ENUM = kLINEAR, //! Two wide channel vectorized row major format. This format is bound to //! FP16. It is only available for dimensions >= 3. @@ -241,9 +207,6 @@ enum class TensorFormat : int32_t //! [N][(C+1)/2][H][W][2], with the tensor coordinates (n, c, h, w) //! mapping to array subscript [n][c/2][h][w][c%2]. kCHW2 = 1, - //! Deprecated name of kCHW2, provided for backwards compatibility and will - //! be removed in TensorRT 8.0. - kNC2HW2 TRT_DEPRECATED_ENUM = kCHW2, //! Eight channel format where C is padded to a multiple of 8. This format //! is bound to FP16. It is only available for dimensions >= 3. @@ -252,9 +215,6 @@ enum class TensorFormat : int32_t //! [N][H][W][(C+7)/8*8], with the tensor coordinates (n, c, h, w) //! mapping to array subscript [n][h][w][c]. kHWC8 = 2, - //! Deprecated name of kHWC8, provided for backwards compatibility and will - //! be removed in TensorRT 8.0. - kNHWC8 TRT_DEPRECATED_ENUM = kHWC8, //! Four wide channel vectorized row major format. This format is bound to //! INT8 or FP16. It is only available for dimensions >= 3. @@ -280,8 +240,9 @@ enum class TensorFormat : int32_t //! [N][(C+15)/16][H][W][16], with the tensor coordinates (n, c, h, w) //! mapping to array subscript [n][c/16][h][w][c%16]. //! - //! For DLA usage, this format maps to the native format for FP16, + //! For DLA usage, this format maps to the native image format for FP16, //! and the tensor sizes are limited to C,H,W in the range [1,8192]. + //! kCHW16 = 4, //! Thirty-two wide channel vectorized row major format. This format is @@ -291,7 +252,7 @@ enum class TensorFormat : int32_t //! [N][(C+31)/32][H][W][32], with the tensor coordinates (n, c, h, w) //! mapping to array subscript [n][c/32][h][w][c%32]. //! - //! For DLA usage, this format maps to the native format for INT8, + //! For DLA usage, this format maps to the native image format for INT8, //! and the tensor sizes are limited to C,H,W in the range [1,8192]. kCHW32 = 5, @@ -315,7 +276,7 @@ enum class TensorFormat : int32_t //! and is only available for dimensions >= 3. kHWC = 8, - //! DLA planar format. For a tensor with dimension {N, C, H, W}, the W axis + //! DLA planar format. For a tensor with dimension {N, C, H, W}, the W axis //! always has unit stride. The stride for stepping along the H axis is //! rounded up to 64 bytes. //! @@ -325,7 +286,7 @@ enum class TensorFormat : int32_t //! mapping to array subscript [n][c][h][w]. kDLA_LINEAR = 9, - //! DLA image format. For a tensor with dimension {N, C, H, W} the C axis + //! DLA image format. For a tensor with dimension {N, C, H, W} the C axis //! always has unit stride. The stride for stepping along the H axis is rounded up //! to 32 bytes. C can only be 1, 3 or 4. //! If C == 1, it will map to grayscale format. @@ -337,22 +298,34 @@ enum class TensorFormat : int32_t //! [N][H][roundUp(W, 32/C'/elementSize)][C'] where elementSize is 2 for FP16 //! and 1 for Int8. The tensor coordinates (n, c, h, w) mapping to array //! subscript [n][h][w][c]. - kDLA_HWC4 = 10 + kDLA_HWC4 = 10, + + //! Sixteen channel format where C is padded to a multiple of 16. This format + //! is bound to FP16. It is only available for dimensions >= 3. + //! For a tensor with dimensions {N, C, H, W}, + //! the memory layout is equivalent to the array with dimensions + //! [N][H][W][(C+15)/16*16], with the tensor coordinates (n, c, h, w) + //! mapping to array subscript [n][h][w][c]. + kHWC16 = 11 }; //! //! \brief PluginFormat is reserved for backward compatibility. //! -//! \see IPluginExt::getPluginFormats() +//! \see IPluginV2::supportsFormat() //! using PluginFormat = TensorFormat; +namespace impl +{ //! Maximum number of elements in TensorFormat enum. \see TensorFormat template <> -constexpr inline int32_t EnumMax() +struct EnumMaxImpl { - return 11; -} + //! Declaration of kVALUE that represents maximum number of elements in TensorFormat enum + static constexpr int32_t kVALUE = 12; +}; +} // namespace impl //! \struct PluginTensorDesc //! @@ -361,14 +334,18 @@ constexpr inline int32_t EnumMax() //! Scale is only valid when data type is DataType::kINT8. TensorRT will set //! the value to -1.0f if it is invalid. //! -//! \see IPluginV2IOExt::supportsFormat +//! \see IPluginV2IOExt::supportsFormatCombination //! \see IPluginV2IOExt::configurePlugin //! struct PluginTensorDesc { + //! Dimensions. Dims dims; - DataType type; //!< \warning DataType:kBOOL not supported. + //! \warning DataType:kBOOL not supported. + DataType type; + //! Tensor format. TensorFormat format; + //! Scale for INT8 data type. float scale; }; @@ -380,10 +357,14 @@ struct PluginTensorDesc //! enum class PluginVersion : uint8_t { - kV2 = 0, //! IPluginV2 - kV2_EXT = 1, //! IPluginV2Ext - kV2_IOEXT = 2, //! IPluginV2IOExt - kV2_DYNAMICEXT = 3, //! IPluginV2DynamicExt + //! IPluginV2 + kV2 = 0, + //! IPluginV2Ext + kV2_EXT = 1, + //! IPluginV2IOExt + kV2_IOEXT = 2, + //! IPluginV2DynamicExt + kV2_DYNAMICEXT = 3, }; //! \class IPluginV2 @@ -403,10 +384,9 @@ public: //! //! \brief Return the API version with which this plugin was built. //! - //! Do not override this method as it is used by the TensorRT library to maintain backwards-compatibility with - //! plugins. + //! Do not override this method as it is used by the TensorRT library to maintain backwards-compatibility with plugins. //! - virtual int32_t getTensorRTVersion() const TRTNOEXCEPT + virtual int32_t getTensorRTVersion() const noexcept { return NV_TENSORRT_VERSION; } @@ -415,13 +395,13 @@ public: //! \brief Return the plugin type. Should match the plugin name returned by the corresponding plugin creator //! \see IPluginCreator::getPluginName() //! - virtual const char* getPluginType() const TRTNOEXCEPT = 0; + virtual AsciiChar const* getPluginType() const noexcept = 0; //! //! \brief Return the plugin version. Should match the plugin version returned by the corresponding plugin creator //! \see IPluginCreator::getPluginVersion() //! - virtual const char* getPluginVersion() const TRTNOEXCEPT = 0; + virtual AsciiChar const* getPluginVersion() const noexcept = 0; //! //! \brief Get the number of outputs from the layer. @@ -431,7 +411,7 @@ public: //! This function is called by the implementations of INetworkDefinition and IBuilder. In particular, it is called //! prior to any call to initialize(). //! - virtual int32_t getNbOutputs() const TRTNOEXCEPT = 0; + virtual int32_t getNbOutputs() const noexcept = 0; //! //! \brief Get the dimension of an output tensor. @@ -443,7 +423,7 @@ public: //! This function is called by the implementations of INetworkDefinition and IBuilder. In particular, it is called //! prior to any call to initialize(). //! - virtual Dims getOutputDimensions(int32_t index, const Dims* inputs, int32_t nbInputDims) TRTNOEXCEPT = 0; + virtual Dims getOutputDimensions(int32_t index, Dims const* inputs, int32_t nbInputDims) noexcept = 0; //! //! \brief Check format support. @@ -462,7 +442,7 @@ public: //! //! \warning DataType:kBOOL not supported. //! - virtual bool supportsFormat(DataType type, PluginFormat format) const TRTNOEXCEPT = 0; + virtual bool supportsFormat(DataType type, PluginFormat format) const noexcept = 0; //! //! \brief Configure the layer. @@ -487,21 +467,22 @@ public: //! //! \warning DataType:kBOOL not supported. //! - virtual void configureWithFormat(const Dims* inputDims, int32_t nbInputs, const Dims* outputDims, int32_t nbOutputs, - DataType type, PluginFormat format, int32_t maxBatchSize) TRTNOEXCEPT = 0; + virtual void configureWithFormat(Dims const* inputDims, int32_t nbInputs, Dims const* outputDims, int32_t nbOutputs, + DataType type, PluginFormat format, int32_t maxBatchSize) noexcept = 0; //! //! \brief Initialize the layer for execution. This is called when the engine is created. //! //! \return 0 for success, else non-zero (which will cause engine termination). //! - virtual int32_t initialize() TRTNOEXCEPT = 0; + virtual int32_t initialize() noexcept = 0; //! //! \brief Release resources acquired during plugin layer initialization. This is called when the engine is - //! destroyed. \see initialize() + //! destroyed. + //! \see initialize() //! - virtual void terminate() TRTNOEXCEPT = 0; + virtual void terminate() noexcept = 0; //! //! \brief Find the workspace size required by the layer. @@ -511,7 +492,7 @@ public: //! //! \return The workspace size. //! - virtual size_t getWorkspaceSize(int32_t maxBatchSize) const TRTNOEXCEPT = 0; + virtual size_t getWorkspaceSize(int32_t maxBatchSize) const noexcept = 0; //! //! \brief Execute the layer. @@ -524,48 +505,61 @@ public: //! //! \return 0 for success, else non-zero (which will cause engine termination). //! - virtual int32_t enqueue(int32_t batchSize, const void* const* inputs, void** outputs, void* workspace, - cudaStream_t stream) TRTNOEXCEPT = 0; + virtual int32_t enqueue(int32_t batchSize, void const* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept + = 0; //! //! \brief Find the size of the serialization buffer required. //! //! \return The size of the serialization buffer. //! - virtual size_t getSerializationSize() const TRTNOEXCEPT = 0; + virtual size_t getSerializationSize() const noexcept = 0; //! //! \brief Serialize the layer. //! - //! \param buffer A pointer to a buffer to serialize data. Size of buffer must be equal to value returned by getSerializationSize. + //! \param buffer A pointer to a buffer to serialize data. Size of buffer must be equal to value returned by + //! getSerializationSize. //! //! \see getSerializationSize() //! - virtual void serialize(void* buffer) const TRTNOEXCEPT = 0; + virtual void serialize(void* buffer) const noexcept = 0; //! //! \brief Destroy the plugin object. This will be called when the network, builder or engine is destroyed. //! - virtual void destroy() TRTNOEXCEPT = 0; + virtual void destroy() noexcept = 0; //! - //! \brief Clone the plugin object. This copies over internal plugin parameters and returns a new plugin object with these parameters. + //! \brief Clone the plugin object. This copies over internal plugin parameters and returns a new plugin object with + //! these parameters. //! - virtual IPluginV2* clone() const TRTNOEXCEPT = 0; + //! The TensorRT runtime calls clone() to clone the plugin when an execution context is created for an engine, + //! after the engine has been created. The runtime does not call initialize() on the cloned plugin, + //! so the cloned plugin should be created in an initialized state. + //! + virtual IPluginV2* clone() const noexcept = 0; //! //! \brief Set the namespace that this plugin object belongs to. Ideally, all plugin //! objects from the same plugin library should have the same namespace. //! - virtual void setPluginNamespace(const char* pluginNamespace) TRTNOEXCEPT = 0; + virtual void setPluginNamespace(AsciiChar const* pluginNamespace) noexcept = 0; //! //! \brief Return the namespace of the plugin object. //! - virtual const char* getPluginNamespace() const TRTNOEXCEPT = 0; + virtual AsciiChar const* getPluginNamespace() const noexcept = 0; + + IPluginV2() = default; + virtual ~IPluginV2() noexcept = default; protected: - virtual ~IPluginV2() {} + IPluginV2(IPluginV2 const&) = default; + IPluginV2(IPluginV2&&) = default; + IPluginV2& operator=(IPluginV2 const&) & = default; + IPluginV2& operator=(IPluginV2&&) & = default; }; //! \class IPluginV2Ext @@ -583,13 +577,14 @@ class IPluginV2Ext : public IPluginV2 public: //! //! \brief Return the DataType of the plugin output at the requested index. - //! The default behavior should be to return the type of the first input, or DataType::kFLOAT if the layer has no - //! inputs. The returned data type must have a format that is supported by the plugin. \see supportsFormat() + //! The default behavior should be to return the type of the first input, or DataType::kFLOAT if the layer has no inputs. + //! The returned data type must have a format that is supported by the plugin. + //! \see supportsFormat() //! //! \warning DataType:kBOOL not supported. //! virtual nvinfer1::DataType getOutputDataType( - int32_t index, const nvinfer1::DataType* inputTypes, int32_t nbInputs) const TRTNOEXCEPT = 0; + int32_t index, nvinfer1::DataType const* inputTypes, int32_t nbInputs) const noexcept = 0; //! \brief Return true if output tensor is broadcast across a batch. //! @@ -602,7 +597,7 @@ public: //! physical replication of the values. //! virtual bool isOutputBroadcastAcrossBatch( - int32_t outputIndex, const bool* inputIsBroadcasted, int32_t nbInputs) const TRTNOEXCEPT = 0; + int32_t outputIndex, bool const* inputIsBroadcasted, int32_t nbInputs) const noexcept = 0; //! \brief Return true if plugin can use input that is broadcast across batch without replication. //! @@ -617,7 +612,7 @@ public: //! //! This method is called only for inputs that can be broadcast. //! - virtual bool canBroadcastInputAcrossBatch(int32_t inputIndex) const TRTNOEXCEPT = 0; + virtual bool canBroadcastInputAcrossBatch(int32_t inputIndex) const noexcept = 0; //! //! \brief Configure the layer with input and output data types. @@ -638,68 +633,83 @@ public: //! //! The dimensions passed here do not include the outermost batch size (i.e. for 2-D image networks, they will be //! 3-dimensional CHW dimensions). When inputIsBroadcast or outputIsBroadcast is true, the outermost batch size for - //! that input or output should be treated as if it is one. \ref inputIsBroadcast[i] is true only if the input is - //! semantically broadcast across the batch and \ref canBroadcastInputAcrossBatch(i) returned true. \ref - //! outputIsBroadcast[i] is true only if \ref isOutputBroadcastAcrossBatch(i) returned true. + //! that input or output should be treated as if it is one. + //! \ref inputIsBroadcast[i] is true only if the input is semantically broadcast across the batch and + //! \ref canBroadcastInputAcrossBatch(i) returned true. + //! \ref outputIsBroadcast[i] is true only if \ref isOutputBroadcastAcrossBatch(i) returns true. //! //! \warning for the floatFormat field, the values PluginFormat::kCHW4, PluginFormat::kCHW16, and //! PluginFormat::kCHW32 will not be passed in, this is to keep backward compatibility with TensorRT 5.x series. Use //! PluginV2IOExt or PluginV2DynamicExt for other PluginFormats. //! - virtual void configurePlugin(const Dims* inputDims, int32_t nbInputs, const Dims* outputDims, int32_t nbOutputs, - const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int32_t maxBatchSize) TRTNOEXCEPT = 0; + virtual void configurePlugin(Dims const* inputDims, int32_t nbInputs, Dims const* outputDims, int32_t nbOutputs, + DataType const* inputTypes, DataType const* outputTypes, bool const* inputIsBroadcast, + bool const* outputIsBroadcast, PluginFormat floatFormat, int32_t maxBatchSize) noexcept = 0; - virtual ~IPluginV2Ext() {} + IPluginV2Ext() = default; + ~IPluginV2Ext() override = default; //! //! \brief Attach the plugin object to an execution context and grant the plugin the access to some context resource. //! - //! \param cudnn The cudnn context handle of the execution context + //! \param cudnn The CUDNN context handle of the execution context //! \param cublas The cublas context handle of the execution context //! \param allocator The allocator used by the execution context //! - //! This function is called automatically for each plugin when a new execution context is created. + //! This function is called automatically for each plugin when a new execution context is created. If the context + //! was created without resources, this method is not called until the resources are assigned. It is also called if + //! new resources are assigned to the context. + //! //! If the plugin needs per-context resource, it can be allocated here. //! The plugin can also get context-owned CUDNN and CUBLAS context here. //! - virtual void attachToContext(cudnnContext* /*cudnn*/, cublasContext* /*cublas*/, IGpuAllocator* /*allocator*/) TRTNOEXCEPT {} + //! \note In the automotive safety context, the CUDNN and CUBLAS parameters will be nullptr because CUDNN and CUBLAS + //! is not used by the safe runtime. + //! + virtual void attachToContext(cudnnContext* /*cudnn*/, cublasContext* /*cublas*/, IGpuAllocator* /*allocator*/) noexcept {} //! //! \brief Detach the plugin object from its execution context. //! - //! This function is called automatically for each plugin when a execution context is destroyed. + //! This function is called automatically for each plugin when a execution context is destroyed or the context + //! resources are unassigned from the context. + //! //! If the plugin owns per-context resource, it can be released here. //! - virtual void detachFromContext() TRTNOEXCEPT {} + virtual void detachFromContext() noexcept {} //! //! \brief Clone the plugin object. This copies over internal plugin parameters as well and returns a new plugin object with these parameters. //! If the source plugin is pre-configured with configurePlugin(), the returned object should also be pre-configured. The returned object should allow attachToContext() with a new execution context. //! Cloned plugin objects can share the same per-engine immutable resource (e.g. weights) with the source object (e.g. via ref-counting) to avoid duplication. //! - virtual IPluginV2Ext* clone() const _TENSORRT_OVERRIDE TRTNOEXCEPT = 0; + IPluginV2Ext* clone() const noexcept override = 0; protected: + IPluginV2Ext(IPluginV2Ext const&) = default; + IPluginV2Ext(IPluginV2Ext&&) = default; + IPluginV2Ext& operator=(IPluginV2Ext const&) & = default; + IPluginV2Ext& operator=(IPluginV2Ext&&) & = default; + //! //! \brief Return the API version with which this plugin was built. The - //! upper byte reserved by TensorRT and is used to differentiate this from IPlguinV2. + //! upper byte reserved by TensorRT and is used to differentiate this from IPluginV2. //! //! Do not override this method as it is used by the TensorRT library to maintain backwards-compatibility with //! plugins. //! - int32_t getTensorRTVersion() const _TENSORRT_OVERRIDE TRTNOEXCEPT + int32_t getTensorRTVersion() const noexcept override { - return (static_cast(PluginVersion::kV2_EXT) << 24 | (NV_TENSORRT_VERSION & 0xFFFFFF)); + return static_cast((static_cast(PluginVersion::kV2_EXT) << 24U) + | (static_cast(NV_TENSORRT_VERSION) & 0xFFFFFFU)); } //! //! \brief Derived classes should not implement this. In a C++11 API it would be override final. //! - void configureWithFormat(const Dims* /*inputDims*/, int32_t /*nbInputs*/, const Dims* /*outputDims*/, - int32_t /*nbOutputs*/, DataType /*type*/, PluginFormat /*format*/, - int32_t /*maxBatchSize*/) _TENSORRT_OVERRIDE TRTNOEXCEPT + void configureWithFormat(Dims const* /*inputDims*/, int32_t /*nbInputs*/, Dims const* /*outputDims*/, + int32_t /*nbOutputs*/, DataType /*type*/, PluginFormat /*format*/, int32_t /*maxBatchSize*/) noexcept override { } }; @@ -728,7 +738,7 @@ public: //! \param nbOutput Number of output tensors. //! virtual void configurePlugin( - const PluginTensorDesc* in, int32_t nbInput, const PluginTensorDesc* out, int32_t nbOutput) TRTNOEXCEPT = 0; + PluginTensorDesc const* in, int32_t nbInput, PluginTensorDesc const* out, int32_t nbOutput) noexcept = 0; //! //! \brief Return true if plugin supports the format and datatype for the input/output indexed by pos. @@ -752,8 +762,8 @@ public: //! * A definition for a plugin that supports only FP16 NCHW for its two inputs, //! and FP32 NCHW for its single output: //! - //! return inOut.format[pos] == TensorFormat::kLINEAR && (inOut.type[pos] == pos < 2 ? DataType::kHALF : - //! DataType::kFLOAT); + //! return inOut.format[pos] == TensorFormat::kLINEAR && + //! (inOut.type[pos] == pos < 2 ? DataType::kHALF : DataType::kFLOAT); //! //! * A definition for a "polymorphic" plugin with two inputs and one output that supports //! any format or type, but the inputs and output must have the same format and type: @@ -763,56 +773,39 @@ public: //! Warning: TensorRT will stop asking for formats once it finds kFORMAT_COMBINATION_LIMIT on combinations. //! virtual bool supportsFormatCombination( - int32_t pos, const PluginTensorDesc* inOut, int32_t nbInputs, int32_t nbOutputs) const TRTNOEXCEPT = 0; + int32_t pos, PluginTensorDesc const* inOut, int32_t nbInputs, int32_t nbOutputs) const noexcept = 0; + + IPluginV2IOExt() = default; + ~IPluginV2IOExt() override = default; protected: + IPluginV2IOExt(IPluginV2IOExt const&) = default; + IPluginV2IOExt(IPluginV2IOExt&&) = default; + IPluginV2IOExt& operator=(IPluginV2IOExt const&) & = default; + IPluginV2IOExt& operator=(IPluginV2IOExt&&) & = default; + //! //! \brief Return the API version with which this plugin was built. The upper byte is reserved by TensorRT and is - //! used to differentiate this from IPlguinV2 and IPluginV2Ext. + //! used to differentiate this from IPluginV2 and IPluginV2Ext. //! //! Do not override this method as it is used by the TensorRT library to maintain backwards-compatibility with //! plugins. //! - //! \deprecated Deprecated interface will be removed in TensorRT 8.0. - //! - TRT_DEPRECATED - int32_t getTensorRTVersion() const _TENSORRT_OVERRIDE + int32_t getTensorRTVersion() const noexcept override { - return (static_cast(PluginVersion::kV2_IOEXT) << 24 | (NV_TENSORRT_VERSION & 0xFFFFFF)); + return static_cast((static_cast(PluginVersion::kV2_IOEXT) << 24U) + | (static_cast(NV_TENSORRT_VERSION) & 0xFFFFFFU)); } - //! - //! \brief Deprecated interface inheriting from base class. Derived classes should not implement this. In a C++11 - //! API it would be override final. - //! - //! \deprecated Deprecated interface will be removed in TensorRT 8.0. - //! - TRT_DEPRECATED - void configureWithFormat( - const Dims*, int32_t, const Dims*, int32_t, DataType, PluginFormat, int32_t) _TENSORRT_OVERRIDE _TENSORRT_FINAL +private: + // Following are obsolete base class methods, and must not be implemented or used. + + void configurePlugin(Dims const*, int32_t, Dims const*, int32_t, DataType const*, DataType const*, bool const*, + bool const*, PluginFormat, int32_t) noexcept override final { } - //! - //! \brief Deprecated interface inheriting from base class. Derived classes should not implement this. In a C++11 - //! API it would be override final. - //! - //! \deprecated Deprecated interface will be removed in TensorRT 8.0. - //! - TRT_DEPRECATED - void configurePlugin(const Dims*, int32_t, const Dims*, int32_t, const DataType*, const DataType*, const bool*, - const bool*, PluginFormat, int32_t) _TENSORRT_OVERRIDE _TENSORRT_FINAL - { - } - - //! - //! \brief Deprecated interface inheriting from base class. Derived classes should not implement this. In a C++11 - //! API it would be override final. - //! - //! \deprecated Deprecated interface will be removed in TensorRT 8.0. - //! - TRT_DEPRECATED - bool supportsFormat(DataType, PluginFormat) const _TENSORRT_OVERRIDE _TENSORRT_FINAL + bool supportsFormat(DataType, PluginFormat) const noexcept override final { return false; } @@ -825,14 +818,23 @@ protected: enum class PluginFieldType : int32_t { - kFLOAT16 = 0, //!< FP16 field type. - kFLOAT32 = 1, //!< FP32 field type. - kFLOAT64 = 2, //!< FP64 field type. - kINT8 = 3, //!< INT8 field type. - kINT16 = 4, //!< INT16 field type. - kINT32 = 5, //!< INT32 field type. - kCHAR = 6, //!< char field type. - kDIMS = 7, //!< nvinfer1::Dims field type. + //! FP16 field type. + kFLOAT16 = 0, + //! FP32 field type. + kFLOAT32 = 1, + //! FP64 field type. + kFLOAT64 = 2, + //! INT8 field type. + kINT8 = 3, + //! INT16 field type. + kINT16 = 4, + //! INT32 field type. + kINT32 = 5, + //! char field type. + kCHAR = 6, + //! nvinfer1::Dims field type. + kDIMS = 7, + //! Unknown field type. kUNKNOWN = 8 }; @@ -849,22 +851,23 @@ public: //! //! \brief Plugin field attribute name //! - const char* name{nullptr}; + AsciiChar const* name; //! //! \brief Plugin field attribute data //! - const void* data{nullptr}; + void const* data; //! //! \brief Plugin field attribute type //! \see PluginFieldType //! - PluginFieldType type{PluginFieldType::kUNKNOWN}; + PluginFieldType type; //! //! \brief Number of data entries in the Plugin attribute //! - int32_t length{0}; + int32_t length; - PluginField(const char* name_ = nullptr, const void* data_ = nullptr, const PluginFieldType type_ = PluginFieldType::kUNKNOWN, int32_t length_ = 0) + PluginField(AsciiChar const* const name_ = nullptr, void const* const data_ = nullptr, + PluginFieldType const type_ = PluginFieldType::kUNKNOWN, int32_t const length_ = 0) noexcept : name(name_) , data(data_) , type(type_) @@ -873,10 +876,13 @@ public: } }; +//! Plugin field collection struct. struct PluginFieldCollection { - int32_t nbFields; //!< Number of PluginField entries - const PluginField* fields; //!< Pointer to PluginField entries + //! Number of PluginField entries. + int32_t nbFields; + //! Pointer to PluginField entries. + PluginField const* fields; }; //! @@ -893,7 +899,7 @@ public: //! //! \brief Return the version of the API the plugin creator was compiled with. //! - virtual int32_t getTensorRTVersion() const TRTNOEXCEPT + virtual int32_t getTensorRTVersion() const noexcept { return NV_TENSORRT_VERSION; } @@ -901,28 +907,28 @@ public: //! //! \brief Return the plugin name. //! - virtual const char* getPluginName() const TRTNOEXCEPT = 0; + virtual AsciiChar const* getPluginName() const noexcept = 0; //! //! \brief Return the plugin version. //! - virtual const char* getPluginVersion() const TRTNOEXCEPT = 0; + virtual AsciiChar const* getPluginVersion() const noexcept = 0; //! //! \brief Return a list of fields that needs to be passed to createPlugin. //! \see PluginFieldCollection //! - virtual const PluginFieldCollection* getFieldNames() TRTNOEXCEPT = 0; + virtual PluginFieldCollection const* getFieldNames() noexcept = 0; //! //! \brief Return a plugin object. Return nullptr in case of error. //! - virtual IPluginV2* createPlugin(const char* name, const PluginFieldCollection* fc) TRTNOEXCEPT = 0; + virtual IPluginV2* createPlugin(AsciiChar const* name, PluginFieldCollection const* fc) noexcept = 0; //! //! \brief Called during deserialization of plugin layer. Return a plugin object. //! - virtual IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) TRTNOEXCEPT = 0; + virtual IPluginV2* deserializePlugin(AsciiChar const* name, void const* serialData, size_t serialLength) noexcept = 0; //! //! \brief Set the namespace of the plugin creator based on the plugin @@ -930,14 +936,21 @@ public: //! //! \see IPluginRegistry::registerCreator() //! - virtual void setPluginNamespace(const char* pluginNamespace) TRTNOEXCEPT = 0; + virtual void setPluginNamespace(AsciiChar const* pluginNamespace) noexcept = 0; //! //! \brief Return the namespace of the plugin creator object. //! - virtual const char* getPluginNamespace() const TRTNOEXCEPT = 0; + virtual AsciiChar const* getPluginNamespace() const noexcept = 0; - virtual ~IPluginCreator() {} + IPluginCreator() = default; + virtual ~IPluginCreator() = default; + +protected: + IPluginCreator(IPluginCreator const&) = default; + IPluginCreator(IPluginCreator&&) = default; + IPluginCreator& operator=(IPluginCreator const&) & = default; + IPluginCreator& operator=(IPluginCreator&&) & = default; }; //! @@ -954,6 +967,9 @@ public: //! //! \warning Do not inherit from this class, as doing so will break forward-compatibility of the API and ABI. //! +//! \warning In the automotive safety context, be sure to call IPluginRegistry::setErrorRecorder() to register +//! an error recorder with the registry before using other methods in the registry. +//! class IPluginRegistry { @@ -962,22 +978,30 @@ public: //! \brief Register a plugin creator. Returns false if one with same type //! is already registered. //! - virtual bool registerCreator(IPluginCreator& creator, const char* pluginNamespace) noexcept = 0; + virtual bool registerCreator(IPluginCreator& creator, AsciiChar const* const pluginNamespace) noexcept = 0; //! //! \brief Return all the registered plugin creators and the number of //! registered plugin creators. Returns nullptr if none found. //! - virtual IPluginCreator* const* getPluginCreatorList(int32_t* numCreators) const noexcept = 0; + virtual IPluginCreator* const* getPluginCreatorList(int32_t* const numCreators) const noexcept = 0; //! - //! \brief Return plugin creator based on plugin type, version and + //! \brief Return plugin creator based on plugin name, version, and //! namespace associated with plugin during network creation. //! - virtual IPluginCreator* getPluginCreator(const char* pluginType, const char* pluginVersion, const char* pluginNamespace = "") noexcept = 0; + virtual IPluginCreator* getPluginCreator( + AsciiChar const* const pluginName, AsciiChar const* const pluginVersion, AsciiChar const* const pluginNamespace = "") noexcept + = 0; + + IPluginRegistry() = default; + IPluginRegistry(IPluginRegistry const&) = delete; + IPluginRegistry(IPluginRegistry&&) = delete; + IPluginRegistry& operator=(IPluginRegistry const&) & = delete; + IPluginRegistry& operator=(IPluginRegistry&&) & = delete; protected: - virtual ~IPluginRegistry() noexcept {} + virtual ~IPluginRegistry() noexcept = default; public: //! @@ -990,12 +1014,12 @@ public: //! //! \param recorder The error recorder to register with this interface. // - //! \see getErrorRecorder + //! \see getErrorRecorder() //! - virtual void setErrorRecorder(IErrorRecorder* recorder) noexcept = 0; + virtual void setErrorRecorder(IErrorRecorder* const recorder) noexcept = 0; //! - //! \brief set the ErrorRecorder assigned to this interface. + //! \brief Set the ErrorRecorder assigned to this interface. //! //! Retrieves the assigned error recorder object for the given class. A default error recorder does not exist, //! so a nullptr will be returned if setErrorRecorder has not been called, or an ErrorRecorder has not been @@ -1003,27 +1027,40 @@ public: //! //! \return A pointer to the IErrorRecorder object that has been registered. //! - //! \see setErrorRecorder + //! \see setErrorRecorder() //! virtual IErrorRecorder* getErrorRecorder() const noexcept = 0; + + //! + //! \brief Deregister a previously registered plugin creator. + //! + //! Since there may be a desire to limit the number of plugins, + //! this function provides a mechanism for removing plugin creators registered in TensorRT. + //! The plugin creator that is specified by \p creator is removed from TensorRT and no longer tracked. + //! + //! \return True if the plugin creator was deregistered, false if it was not found in the registry or otherwise + //! could + //! not be deregistered. + //! + virtual bool deregisterCreator(IPluginCreator const& creator) noexcept = 0; }; -//! -//! \enum TensorLocation -//! \brief The location for tensor data storage, device or host. -//! -enum class TensorLocation : int32_t +enum class AllocatorFlag : int32_t { - kDEVICE = 0, //!< Data stored on device. - kHOST = 1, //!< Data stored on host. + kRESIZABLE = 0, //!< TensorRT may call realloc() on this allocation }; -//! Maximum number of elements in TensorLocation enum. \see TensorLocation +namespace impl +{ +//! Maximum number of elements in AllocatorFlag enum. \see AllocatorFlag template <> -constexpr inline int32_t EnumMax() +struct EnumMaxImpl { - return 2; -} + static constexpr int32_t kVALUE = 1; +}; +} // namespace impl + +using AllocatorFlags = uint32_t; //! //! \class IGpuAllocator @@ -1034,10 +1071,10 @@ class IGpuAllocator { public: //! - //! A callback implemented by the application to handle acquisition of GPU memory. + //! A thread-safe callback implemented by the application to handle acquisition of GPU memory. //! //! \param size The size of the memory required. - //! \param alignment The required alignment of memory. Alignment will zero + //! \param alignment The required alignment of memory. Alignment will be zero //! or a power of 2 not exceeding the alignment guaranteed by cudaMalloc. //! Thus this allocator can be safely implemented with cudaMalloc/cudaFree. //! An alignment value of zero indicates any alignment is acceptable. @@ -1047,22 +1084,69 @@ public: //! //! If an allocation request cannot be satisfied, nullptr should be returned. //! - virtual void* allocate(uint64_t size, uint64_t alignment, uint32_t flags) TRTNOEXCEPT = 0; + //! \note The implementation must guarantee thread safety for concurrent allocate/free/reallocate + //! requests. + //! + virtual void* allocate(uint64_t const size, uint64_t const alignment, AllocatorFlags const flags) noexcept = 0; //! - //! A callback implemented by the application to handle release of GPU memory. + //! A thread-safe callback implemented by the application to handle release of GPU memory. //! //! TensorRT may pass a nullptr to this function if it was previously returned by allocate(). //! //! \param memory The acquired memory. //! - virtual void free(void* memory) TRTNOEXCEPT = 0; + //! \note The implementation must guarantee thread safety for concurrent allocate/free/reallocate + //! requests. + //! + virtual void free(void* const memory) noexcept = 0; //! //! Destructor declared virtual as general good practice for a class with virtual methods. //! TensorRT never calls the destructor for an IGpuAllocator defined by the application. //! - virtual ~IGpuAllocator() {} + virtual ~IGpuAllocator() = default; + IGpuAllocator() = default; + + //! + //! A thread-safe callback implemented by the application to resize an existing allocation. + //! + //! Only allocations which were allocated with AllocatorFlag::kRESIZABLE will be resized. + //! + //! Options are one of: + //! * resize in place leaving min(oldSize, newSize) bytes unchanged and return the original address + //! * move min(oldSize, newSize) bytes to a new location of sufficient size and return its address + //! * return nullptr, to indicate that the request could not be fulfilled. + //! + //! If nullptr is returned, TensorRT will assume that resize() is not implemented, and that the + //! allocation at baseAddr is still valid. + //! + //! This method is made available for use cases where delegating the resize + //! strategy to the application provides an opportunity to improve memory management. + //! One possible implementation is to allocate a large virtual device buffer and + //! progressively commit physical memory with cuMemMap. CU_MEM_ALLOC_GRANULARITY_RECOMMENDED + //! is suggested in this case. + //! + //! TensorRT may call realloc to increase the buffer by relatively small amounts. + //! + //! \param baseAddr the address of the original allocation. + //! \param alignment The alignment used by the original allocation. + //! \param newSize The new memory size required. + //! \return the address of the reallocated memory + //! + //! \note The implementation must guarantee thread safety for concurrent allocate/free/reallocate + //! requests. + //! + virtual void* reallocate(void* baseAddr, uint64_t alignment, uint64_t newSize) noexcept + { + return nullptr; + } + +protected: + IGpuAllocator(IGpuAllocator const&) = default; + IGpuAllocator(IGpuAllocator&&) = default; + IGpuAllocator& operator=(IGpuAllocator const&) & = default; + IGpuAllocator& operator=(IGpuAllocator&&) & = default; }; //! @@ -1070,8 +1154,9 @@ public: //! //! \brief Application-implemented logging interface for the builder, engine and runtime. //! -//! Note that although a logger is passed on creation to each instance of a IBuilder or safe::IRuntime interface, the logger is internally considered a singleton, and thus -//! multiple instances of safe::IRuntime and/or IBuilder must all use the same logger. +//! Note that although a logger is passed on creation to each instance of a IBuilder or IRuntime interfaces, the logger +//! is internally considered a singleton, and thus multiple instances of IRuntime and/or IBuilder must all use the same +//! logger. //! class ILogger { @@ -1083,11 +1168,16 @@ public: //! enum class Severity : int32_t { - kINTERNAL_ERROR = 0, //!< Internal error has occurred. Execution is unrecoverable. - kERROR = 1, //!< Application error has occurred. - kWARNING = 2, //!< Application error has been discovered. TensorRT has recovered or fallen back to a default. - kINFO = 3, //!< Informational messages with instructional information. - kVERBOSE = 4, //!< Verbose messages with debugging information. + //! An internal error has occurred. Execution is unrecoverable. + kINTERNAL_ERROR = 0, + //! An application error has occurred. + kERROR = 1, + //! An application error has been discovered, but TensorRT has recovered or fallen back to a default. + kWARNING = 2, + //! Informational messages with instructional information. + kINFO = 3, + //! Verbose messages with debugging information. + kVERBOSE = 4, }; //! @@ -1096,17 +1186,28 @@ public: //! \param severity The severity of the message. //! \param msg The log message, null terminated. //! - virtual void log(Severity severity, const char* msg) TRTNOEXCEPT = 0; + virtual void log(Severity severity, AsciiChar const* msg) noexcept = 0; - virtual ~ILogger() {} + ILogger() = default; + virtual ~ILogger() = default; + +protected: + ILogger(ILogger const&) = default; + ILogger(ILogger&&) = default; + ILogger& operator=(ILogger const&) & = default; + ILogger& operator=(ILogger&&) & = default; }; +namespace impl +{ //! Maximum number of elements in ILogger::Severity enum. \see ILogger::Severity template <> -constexpr inline int32_t EnumMax() +struct EnumMaxImpl { - return 5; -} + //! Declaration of kVALUE that represents maximum number of elements in ILogger::Severity enum + static constexpr int32_t kVALUE = 5; +}; +} // namespace impl //! //! \enum ErrorCode @@ -1121,18 +1222,19 @@ enum class ErrorCode : int32_t kSUCCESS = 0, //! - //! An error that does not fall into any other category. This error is included for forward compatibility + //! An error that does not fall into any other category. This error is included for forward compatibility. //! kUNSPECIFIED_ERROR = 1, //! - //! A non-recoverable TensorRT error occurred. + //! A non-recoverable TensorRT error occurred. TensorRT is in an invalid internal state when this error is + //! emitted and any further calls to TensorRT will result in undefined behavior. //! kINTERNAL_ERROR = 2, //! //! An argument passed to the function is invalid in isolation. - //! This is a violation of the API contract + //! This is a violation of the API contract. //! kINVALID_ARGUMENT = 3, @@ -1171,6 +1273,7 @@ enum class ErrorCode : int32_t //! of this error are NaN squashing or integer overflow. In a dynamic system, the data can be thrown away and the //! next frame can be processed or execution can be retried. //! This is either a data corruption error, an input error, or a range error. + //! This is not used in safety but may be used in standard. //! kFAILED_COMPUTATION = 8, @@ -1202,12 +1305,16 @@ enum class ErrorCode : int32_t }; +namespace impl +{ //! Maximum number of elements in ErrorCode enum. \see ErrorCode template <> -constexpr inline int32_t EnumMax() +struct EnumMaxImpl { - return 11; -} + //! Declaration of kVALUE + static constexpr int32_t kVALUE = 11; +}; +} // namespace impl //! //! \class IErrorRecorder @@ -1236,16 +1343,22 @@ public: //! //! A typedef of a c-style string for reporting error descriptions. //! - using ErrorDesc = const char*; + using ErrorDesc = char const*; + + //! + //! The length limit for an error description, excluding the '\0' string terminator. + //! + static constexpr size_t kMAX_DESC_LENGTH = 127U; //! //! A typedef of a 32bit integer for reference counting. //! using RefCount = int32_t; - virtual ~IErrorRecorder() noexcept {}; + IErrorRecorder() = default; + virtual ~IErrorRecorder() noexcept = default; - // Public API’s used to retrieve information from the error recorder. + // Public API used to retrieve information from the error recorder. //! //! \brief Return the number of errors @@ -1314,10 +1427,10 @@ public: //! virtual void clear() noexcept = 0; - // API’s used by TensorRT to report Error information to the application. + // API used by TensorRT to report Error information to the application. //! - //! \brief report an error to the error recorder with the corresponding enum and description. + //! \brief Report an error to the error recorder with the corresponding enum and description. //! //! \param val The error code enum that is being reported. //! \param desc The string description of the error. @@ -1357,54 +1470,21 @@ public: //! virtual RefCount decRefCount() noexcept = 0; +protected: + IErrorRecorder(IErrorRecorder const&) = default; + IErrorRecorder(IErrorRecorder&&) = default; + IErrorRecorder& operator=(IErrorRecorder const&) & = default; + IErrorRecorder& operator=(IErrorRecorder&&) & = default; + }; // class IErrorRecorder } // namespace nvinfer1 -//! -//! Internal C entry point for creating safe::IRuntime. -//! @private -//! -extern "C" TENSORRTAPI void* createSafeInferRuntime_INTERNAL(void* logger, int32_t version); - -//! -//! \brief Return the logger object. -//! -extern "C" TENSORRTAPI nvinfer1::ILogger* getLogger(); - //! //! \brief Return the library version number. //! //! The format is as for TENSORRT_VERSION: (TENSORRT_MAJOR * 1000) + (TENSORRT_MINOR * 100) + TENSOR_PATCH. //! -extern "C" TENSORRTAPI int32_t getInferLibVersion(); - -//! -//! \brief Return the plugin registry -//! -extern "C" TENSORRTAPI nvinfer1::IPluginRegistry* getPluginRegistry(); - -namespace nvinfer1 -{ - -//! -//! \brief Register the plugin creator to the registry -//! The static registry object will be instantiated when the plugin library is -//! loaded. This static object will register all creators available in the -//! library to the registry. -//! -template -class PluginRegistrar -{ -public: - PluginRegistrar() { getPluginRegistry()->registerCreator(instance, ""); } -private: - T instance{}; -}; - -#define REGISTER_TENSORRT_PLUGIN(name) \ - static nvinfer1::PluginRegistrar pluginRegistrar##name {} - -} // namespace nvinfer1 +extern "C" TENSORRTAPI int32_t getInferLibVersion() noexcept; #endif // NV_INFER_RUNTIME_COMMON_H diff --git a/include/NvInferVersion.h b/include/NvInferVersion.h index 807f3cf3..4779594f 100644 --- a/include/NvInferVersion.h +++ b/include/NvInferVersion.h @@ -23,13 +23,13 @@ #ifndef NV_INFER_VERSION_H #define NV_INFER_VERSION_H -#define NV_TENSORRT_MAJOR 7 //!< TensorRT major version. -#define NV_TENSORRT_MINOR 2 //!< TensorRT minor version. -#define NV_TENSORRT_PATCH 3 //!< TensorRT patch version. -#define NV_TENSORRT_BUILD 4 //!< TensorRT build number. +#define NV_TENSORRT_MAJOR 8 //!< TensorRT major version. +#define NV_TENSORRT_MINOR 0 //!< TensorRT minor version. +#define NV_TENSORRT_PATCH 1 //!< TensorRT patch version. +#define NV_TENSORRT_BUILD 6 //!< TensorRT build number. -#define NV_TENSORRT_SONAME_MAJOR 7 //!< Shared object library major version number. -#define NV_TENSORRT_SONAME_MINOR 2 //!< Shared object library minor version number. -#define NV_TENSORRT_SONAME_PATCH 3 //!< Shared object library patch version number. +#define NV_TENSORRT_SONAME_MAJOR 8 //!< Shared object library major version number. +#define NV_TENSORRT_SONAME_MINOR 0 //!< Shared object library minor version number. +#define NV_TENSORRT_SONAME_PATCH 1 //!< Shared object library patch version number. #endif // NV_INFER_VERSION_H diff --git a/include/NvOnnxConfig.h b/include/NvOnnxConfig.h index 46abeeaf..e3a17068 100644 --- a/include/NvOnnxConfig.h +++ b/include/NvOnnxConfig.h @@ -44,24 +44,23 @@ namespace nvonnxparser //! class IOnnxConfig { -protected: - virtual ~IOnnxConfig() {} - public: + virtual ~IOnnxConfig() noexcept = default; //! //! \typedef Verbosity //! \brief Defines Verbosity level. //! - typedef int Verbosity; + typedef int32_t Verbosity; //! //! \brief Set the Model Data Type. //! - //! Sets the Model DataType, one of the following: float -d 32 (default), half precision -d 16, and int8 -d 8 data types. + //! Sets the Model DataType, one of the following: float -d 32 (default), half precision -d 16, and int8 -d 8 data + //! types. //! //! \see getModelDtype() //! - virtual void setModelDtype(const nvinfer1::DataType) TRTNOEXCEPT = 0; + virtual void setModelDtype(const nvinfer1::DataType) noexcept = 0; //! //! \brief Get the Model Data Type. @@ -70,7 +69,7 @@ public: //! //! \see setModelDtype() and #DataType //! - virtual nvinfer1::DataType getModelDtype() const TRTNOEXCEPT = 0; + virtual nvinfer1::DataType getModelDtype() const noexcept = 0; //! //! \brief Get the Model FileName. @@ -79,7 +78,7 @@ public: //! //! \see setModelFileName() //! - virtual const char* getModelFileName() const TRTNOEXCEPT = 0; + virtual const char* getModelFileName() const noexcept = 0; //! //! \brief Set the Model File Name. @@ -92,7 +91,7 @@ public: //! //! \see getModelFileName() //! - virtual void setModelFileName(const char* onnxFilename) TRTNOEXCEPT = 0; + virtual void setModelFileName(const char* onnxFilename) noexcept = 0; //! //! \brief Get the Verbosity Level. @@ -101,27 +100,40 @@ public: //! //! \see addVerbosity(), reduceVerbosity() //! - virtual Verbosity getVerbosityLevel() const TRTNOEXCEPT = 0; + virtual Verbosity getVerbosityLevel() const noexcept = 0; //! //! \brief Increase the Verbosity Level. //! //! \return The Verbosity Level. //! - //! \see addVerbosity(), reduceVerbosity(), setVerbosity(Verbosity) + //! \see reduceVerbosity(), setVerbosity(Verbosity) //! - virtual void addVerbosity() TRTNOEXCEPT = 0; //!< Increase verbosity Level. - virtual void reduceVerbosity() TRTNOEXCEPT = 0; //!< Decrease verbosity Level. - virtual void setVerbosityLevel(Verbosity) TRTNOEXCEPT = 0; //!< Set to specific verbosity Level. + virtual void addVerbosity() noexcept = 0; + + //! + //! \brief Reduce the Verbosity Level. + //! + //! \see addVerbosity(), setVerbosity(Verbosity) + //! + virtual void reduceVerbosity() noexcept = 0; + + //! + //! \brief Set to specific verbosity Level. + //! + //! \see addVerbosity(), reduceVerbosity() + //! + virtual void setVerbosityLevel(Verbosity) noexcept = 0; //! //! \brief Returns the File Name of the Network Description as a Text File. //! - //! \return Return the name of the file containing the network description converted to a plain text, used for debugging purposes. + //! \return Return the name of the file containing the network description converted to a plain text, used for + //! debugging purposes. //! //! \see setTextFilename() //! - virtual const char* getTextFileName() const TRTNOEXCEPT = 0; + virtual const char* getTextFileName() const noexcept = 0; //! //! \brief Set the File Name of the Network Description as a Text File. @@ -134,16 +146,17 @@ public: //! //! \see getTextFilename() //! - virtual void setTextFileName(const char* textFileName) TRTNOEXCEPT = 0; + virtual void setTextFileName(const char* textFileName) noexcept = 0; //! //! \brief Get the File Name of the Network Description as a Text File, including the weights. //! - //! \return Return the name of the file containing the network description converted to a plain text, used for debugging purposes. + //! \return Return the name of the file containing the network description converted to a plain text, used for + //! debugging purposes. //! //! \see setFullTextFilename() //! - virtual const char* getFullTextFileName() const TRTNOEXCEPT = 0; + virtual const char* getFullTextFileName() const noexcept = 0; //! //! \brief Set the File Name of the Network Description as a Text File, including the weights. @@ -156,7 +169,7 @@ public: //! //! \see getFullTextFilename() //! - virtual void setFullTextFileName(const char* fullTextFileName) TRTNOEXCEPT = 0; + virtual void setFullTextFileName(const char* fullTextFileName) noexcept = 0; //! //! \brief Get whether the layer information will be printed. @@ -165,19 +178,23 @@ public: //! //! \see setPrintLayerInfo() //! - virtual bool getPrintLayerInfo() const TRTNOEXCEPT = 0; + virtual bool getPrintLayerInfo() const noexcept = 0; //! //! \brief Set whether the layer information will be printed. //! //! \see getPrintLayerInfo() //! - virtual void setPrintLayerInfo(bool) TRTNOEXCEPT = 0; + virtual void setPrintLayerInfo(bool) noexcept = 0; //! //! \brief Destroy IOnnxConfig object. //! - virtual void destroy() TRTNOEXCEPT = 0; + //! \deprecated Deprecated interface will be removed in TensorRT 10.0. + //! + //! \warning Calling destroy on a managed pointer will result in a double-free error. + //! + TRT_DEPRECATED virtual void destroy() noexcept = 0; }; // class IOnnxConfig diff --git a/include/NvUffParser.h b/include/NvUffParser.h index 6d0996a7..51bd6e84 100644 --- a/include/NvUffParser.h +++ b/include/NvUffParser.h @@ -81,7 +81,7 @@ public: FieldType type = FieldType::kUNKNOWN; int32_t length = 1; - FieldMap(const char* name, const void* data, const FieldType type, int32_t length = 1) TRTNOEXCEPT; + FieldMap(const char* name, const void* data, const FieldType type, int32_t length = 1); }; struct FieldCollection @@ -90,58 +90,6 @@ struct FieldCollection const FieldMap* fields; }; -//! -//! \class IPluginFactory -//! -//! \brief Plugin factory used to configure plugins. -//! -class IPluginFactory -{ -public: - //! - //! \brief A user implemented function that determines if a layer configuration is provided by an IPlugin. - //! - //! \param layerName Name of the layer which the user wishes to validate. - //! - virtual bool isPlugin(const char* layerName) TRTNOEXCEPT = 0; - - //! - //! \brief Creates a plugin. - //! - //! \param layerName Name of layer associated with the plugin. - //! \param weights Weights used for the layer. - //! \param nbWeights Number of weights. - //! \param fc A collection of FieldMaps used as layer parameters for different plugin layers. - //! - //! \see FieldCollection - //! - virtual nvinfer1::IPlugin* createPlugin(const char* layerName, const nvinfer1::Weights* weights, int32_t nbWeights, - const FieldCollection fc) TRTNOEXCEPT = 0; - - virtual ~IPluginFactory() {} -}; - -//! -//! \class IPluginFactoryExt -//! -//! \brief Plugin factory used to configure plugins with added support for TRT versioning. -//! -class IPluginFactoryExt : public IPluginFactory -{ -public: - virtual int32_t getVersion() const TRTNOEXCEPT - { - return NV_TENSORRT_VERSION; - } - - //! - //! \brief A user implemented function that determines if a layer configuration is provided by an IPluginExt. - //! - //! \param layerName Name of the layer which the user wishes to validate. - //! - virtual bool isPluginExt(const char* layerName) TRTNOEXCEPT = 0; -}; - //! //! \class IUffParser //! @@ -159,14 +107,14 @@ public: //! \param inputDims Input dimensions. //! \param inputOrder Input order on which the framework input was originally. //! - virtual bool registerInput(const char* inputName, nvinfer1::Dims inputDims, UffInputOrder inputOrder) TRTNOEXCEPT = 0; + virtual bool registerInput(const char* inputName, nvinfer1::Dims inputDims, UffInputOrder inputOrder) noexcept = 0; //! //! \brief Register an output name of a UFF network. //! //! \param outputName Output name. //! - virtual bool registerOutput(const char* outputName) TRTNOEXCEPT = 0; + virtual bool registerOutput(const char* outputName) noexcept = 0; //! //! \brief Parse a UFF file. @@ -175,9 +123,9 @@ public: //! \param network Network in which the UFFParser will fill the layers. //! \param weightsType The type on which the weights will transformed in. //! - virtual bool parse(const char* file, - nvinfer1::INetworkDefinition& network, - nvinfer1::DataType weightsType=nvinfer1::DataType::kFLOAT) TRTNOEXCEPT = 0; + virtual bool parse(const char* file, nvinfer1::INetworkDefinition& network, + nvinfer1::DataType weightsType = nvinfer1::DataType::kFLOAT) noexcept + = 0; //! //! \brief Parse a UFF buffer, useful if the file already live in memory. @@ -187,48 +135,36 @@ public: //! \param network Network in which the UFFParser will fill the layers. //! \param weightsType The type on which the weights will transformed in. //! - virtual bool parseBuffer(const char* buffer, std::size_t size, - nvinfer1::INetworkDefinition& network, - nvinfer1::DataType weightsType=nvinfer1::DataType::kFLOAT) TRTNOEXCEPT = 0; + virtual bool parseBuffer(const char* buffer, std::size_t size, nvinfer1::INetworkDefinition& network, + nvinfer1::DataType weightsType = nvinfer1::DataType::kFLOAT) noexcept + = 0; - virtual void destroy() TRTNOEXCEPT = 0; + //! + //! \deprecated Deprecated interface will be removed in TensorRT 10.0. + //! + TRT_DEPRECATED virtual void destroy() noexcept = 0; //! //! \brief Return Version Major of the UFF. //! - virtual int32_t getUffRequiredVersionMajor() TRTNOEXCEPT = 0; + virtual int32_t getUffRequiredVersionMajor() noexcept = 0; //! //! \brief Return Version Minor of the UFF. //! - virtual int32_t getUffRequiredVersionMinor() TRTNOEXCEPT = 0; + virtual int32_t getUffRequiredVersionMinor() noexcept = 0; //! //! \brief Return Patch Version of the UFF. //! - virtual int32_t getUffRequiredVersionPatch() TRTNOEXCEPT = 0; - - //! - //! \brief Set the IPluginFactory used to create the user defined plugins. - //! - //! \param factory Pointer to an instance of the user implmentation of IPluginFactory. - //! - virtual void setPluginFactory(IPluginFactory* factory) TRTNOEXCEPT = 0; - - //! - //! \brief Set the IPluginFactoryExt used to create the user defined pluginExts. - //! - //! \param factory Pointer to an instance of the user implmentation of IPluginFactoryExt. - //! - virtual void setPluginFactoryExt(IPluginFactoryExt* factory) TRTNOEXCEPT = 0; + virtual int32_t getUffRequiredVersionPatch() noexcept = 0; //! //! \brief Set the namespace used to lookup and create plugins in the network. //! - virtual void setPluginNamespace(const char* libNamespace) TRTNOEXCEPT = 0; + virtual void setPluginNamespace(const char* libNamespace) noexcept = 0; -protected: - virtual ~IUffParser() {} + virtual ~IUffParser() noexcept = default; public: //! @@ -239,23 +175,25 @@ public: //! recorder to nullptr unregisters the recorder with the interface, resulting in a call to decRefCount if //! a recorder has been registered. //! + //! If an error recorder is not set, messages will be sent to the global log stream. + //! //! \param recorder The error recorder to register with this interface. // - //! \see getErrorRecorder + //! \see getErrorRecorder() //! - virtual void setErrorRecorder(nvinfer1::IErrorRecorder* recorder) TRTNOEXCEPT = 0; + virtual void setErrorRecorder(nvinfer1::IErrorRecorder* recorder) noexcept = 0; //! //! \brief get the ErrorRecorder assigned to this interface. //! - //! Retrieves the assigned error recorder object for the given class. A default error recorder does not exist, - //! so a nullptr will be returned if setErrorRecorder has not been called. + //! Retrieves the assigned error recorder object for the given class. A + //! nullptr will be returned if setErrorRecorder has not been called. //! //! \return A pointer to the IErrorRecorder object that has been registered. //! - //! \see setErrorRecorder + //! \see setErrorRecorder() //! - virtual nvinfer1::IErrorRecorder* getErrorRecorder() const TRTNOEXCEPT = 0; + virtual nvinfer1::IErrorRecorder* getErrorRecorder() const noexcept = 0; }; //! @@ -265,22 +203,24 @@ public: //! //! \see nvuffparser::IUffParser //! -TENSORRTAPI IUffParser* createUffParser() TRTNOEXCEPT; +//! \deprecated IUffParser will be removed in TensorRT 9.0. Plan to migrate your workflow to +//! use nvonnxparser::IParser for deployment. +//! +TENSORRTAPI IUffParser* createUffParser() noexcept; //! //! \brief Shuts down protocol buffers library. //! //! \note No part of the protocol buffers library can be used after this function is called. //! -TENSORRTAPI void shutdownProtobufLibrary(void) TRTNOEXCEPT; +TENSORRTAPI void shutdownProtobufLibrary(void) noexcept; } // namespace nvuffparser - //! //! Internal C entry point for creating IUffParser //! @private //! -extern "C" TENSORRTAPI void* createNvUffParser_INTERNAL() TRTNOEXCEPT; +extern "C" TENSORRTAPI void* createNvUffParser_INTERNAL() noexcept; #endif /* !NV_UFF_PARSER_H */ diff --git a/include/NvUtils.h b/include/NvUtils.h index ed6f51c2..4631b83c 100644 --- a/include/NvUtils.h +++ b/include/NvUtils.h @@ -72,8 +72,10 @@ namespace utils //! //! \return True on success, false on failure. //! -TENSORRTAPI bool reshapeWeights( - const Weights& input, const int32_t* shape, const int32_t* shapeOrder, void* data, int32_t nbDims); +//! \warning This file will be removed in TensorRT 10.0. +//! +TRT_DEPRECATED TENSORRTAPI bool reshapeWeights( + const Weights& input, int32_t const* shape, int32_t const* shapeOrder, void* data, int32_t nbDims) noexcept; //! //! \param input The input data to re-order. @@ -116,7 +118,10 @@ TENSORRTAPI bool reshapeWeights( //! //! \see reshapeWeights() //! -TENSORRTAPI bool reorderSubBuffers(void* input, const int32_t* order, int32_t num, int32_t size); +//! \warning This file will be removed in TensorRT 10.0. +//! +TRT_DEPRECATED TENSORRTAPI bool reorderSubBuffers( + void* input, int32_t const* order, int32_t num, int32_t size) noexcept; //! //! \param input The input data to transpose. @@ -129,7 +134,10 @@ TENSORRTAPI bool reorderSubBuffers(void* input, const int32_t* order, int32_t nu //! //! \return True on success, false on failure. //! -TENSORRTAPI bool transposeSubBuffers(void* input, DataType type, int32_t num, int32_t height, int32_t width); +//! \warning This file will be removed in TensorRT 10.0. +//! +TRT_DEPRECATED TENSORRTAPI bool transposeSubBuffers( + void* input, DataType type, int32_t num, int32_t height, int32_t width) noexcept; } // namespace utils } // namespace nvinfer1 diff --git a/parsers/caffe/NvCaffeParser.cpp b/parsers/caffe/NvCaffeParser.cpp index 046b87f7..afa96bd2 100644 --- a/parsers/caffe/NvCaffeParser.cpp +++ b/parsers/caffe/NvCaffeParser.cpp @@ -19,17 +19,17 @@ using namespace nvcaffeparser1; -void nvcaffeparser1::shutdownProtobufLibrary() +void nvcaffeparser1::shutdownProtobufLibrary() noexcept { google::protobuf::ShutdownProtobufLibrary(); } -extern "C" void* createNvCaffeParser_INTERNAL() +extern "C" void* createNvCaffeParser_INTERNAL() noexcept { return nvcaffeparser1::createCaffeParser(); } -ICaffeParser* nvcaffeparser1::createCaffeParser() +ICaffeParser* nvcaffeparser1::createCaffeParser() noexcept { return new CaffeParser; } diff --git a/parsers/caffe/binaryProtoBlob.h b/parsers/caffe/binaryProtoBlob.h index 8e4553cc..28cfa36f 100644 --- a/parsers/caffe/binaryProtoBlob.h +++ b/parsers/caffe/binaryProtoBlob.h @@ -26,41 +26,41 @@ namespace nvcaffeparser1 class BinaryProtoBlob : public IBinaryProtoBlob { public: - BinaryProtoBlob(void* memory, nvinfer1::DataType type, nvinfer1::DimsNCHW dimensions) + BinaryProtoBlob(void* memory, nvinfer1::DataType type, nvinfer1::Dims4 dimensions) : mMemory(memory) , mDataType(type) , mDimensions(dimensions) { } - nvinfer1::DimsNCHW getDimensions() override + nvinfer1::Dims4 getDimensions() noexcept override { return mDimensions; } - nvinfer1::DataType getDataType() override + nvinfer1::DataType getDataType() noexcept override { return mDataType; } - const void* getData() override + const void* getData() noexcept override { return mMemory; } - void destroy() override + void destroy() noexcept override { delete this; } - ~BinaryProtoBlob() override + ~BinaryProtoBlob() noexcept override { free(mMemory); } void* mMemory; nvinfer1::DataType mDataType; - nvinfer1::DimsNCHW mDimensions; + nvinfer1::Dims4 mDimensions; }; } // namespace nvcaffeparser1 #endif // TRT_CAFFE_PARSER_BINARY_PROTO_BLOB_H diff --git a/parsers/caffe/blobNameToTensor.h b/parsers/caffe/blobNameToTensor.h index 5c4e80a8..14b59913 100644 --- a/parsers/caffe/blobNameToTensor.h +++ b/parsers/caffe/blobNameToTensor.h @@ -33,7 +33,7 @@ public: mMap[name] = tensor; } - nvinfer1::ITensor* find(const char* name) const override + nvinfer1::ITensor* find(const char* name) const noexcept override { auto p = mMap.find(name); if (p == mMap.end()) diff --git a/parsers/caffe/caffeParser/caffeParser.cpp b/parsers/caffe/caffeParser/caffeParser.cpp index 10600d9f..2c12cc2a 100644 --- a/parsers/caffe/caffeParser/caffeParser.cpp +++ b/parsers/caffe/caffeParser/caffeParser.cpp @@ -66,7 +66,7 @@ std::vector CaffeParser::parseNormalizeParam(const trtcaf // If .caffemodel is not provided, need to randomize the weight if (!weightFactory.isInitialized()) { - int C = parserutils::getCHW(tensors[msg.bottom(0)]->getDimensions()).c(); + int C = parserutils::getC(tensors[msg.bottom(0)]->getDimensions()); w.emplace_back(weightFactory.allocateWeights(C, std::normal_distribution(0.0F, 1.0F))); } else @@ -313,7 +313,7 @@ const IBlobNameToTensor* CaffeParser::parseBuffers(const char* deployBuffer, const char* modelBuffer, std::size_t modelLength, INetworkDefinition& network, - DataType weightType) + DataType weightType) noexcept { mDeploy = std::unique_ptr(new trtcaffe::NetParameter); google::protobuf::io::ArrayInputStream deployStream(deployBuffer, deployLength); @@ -341,7 +341,7 @@ const IBlobNameToTensor* CaffeParser::parseBuffers(const char* deployBuffer, const IBlobNameToTensor* CaffeParser::parse(const char* deployFile, const char* modelFile, INetworkDefinition& network, - DataType weightType) + DataType weightType) noexcept { CHECK_NULL_RET_NULL(deployFile) @@ -391,12 +391,12 @@ const IBlobNameToTensor* CaffeParser::parse(INetworkDefinition& network, { if (mDeploy->input_shape_size()) { - dims = DimsCHW{(int) mDeploy->input_shape().Get(i).dim().Get(1), (int) mDeploy->input_shape().Get(i).dim().Get(2), (int) mDeploy->input_shape().Get(i).dim().Get(3)}; + dims = Dims3{(int) mDeploy->input_shape().Get(i).dim().Get(1), (int) mDeploy->input_shape().Get(i).dim().Get(2), (int) mDeploy->input_shape().Get(i).dim().Get(3)}; } else { // Deprecated, but still used in a lot of networks - dims = DimsCHW{(int) mDeploy->input_dim().Get(i * 4 + 1), (int) mDeploy->input_dim().Get(i * 4 + 2), (int) mDeploy->input_dim().Get(i * 4 + 3)}; + dims = Dims3{(int) mDeploy->input_dim().Get(i * 4 + 1), (int) mDeploy->input_dim().Get(i * 4 + 2), (int) mDeploy->input_dim().Get(i * 4 + 3)}; } } else @@ -404,12 +404,12 @@ const IBlobNameToTensor* CaffeParser::parse(INetworkDefinition& network, std::cout << "Warning, setting batch size to 1. Update the dimension after parsing due to using explicit batch size." << std::endl; if (mDeploy->input_shape_size()) { - dims = DimsNCHW{1, (int) mDeploy->input_shape().Get(i).dim().Get(1), (int) mDeploy->input_shape().Get(i).dim().Get(2), (int) mDeploy->input_shape().Get(i).dim().Get(3)}; + dims = Dims4{1, (int) mDeploy->input_shape().Get(i).dim().Get(1), (int) mDeploy->input_shape().Get(i).dim().Get(2), (int) mDeploy->input_shape().Get(i).dim().Get(3)}; } else { // Deprecated, but still used in a lot of networks - dims = DimsNCHW{1, (int) mDeploy->input_dim().Get(i * 4 + 1), (int) mDeploy->input_dim().Get(i * 4 + 2), (int) mDeploy->input_dim().Get(i * 4 + 3)}; + dims = Dims4{1, (int) mDeploy->input_dim().Get(i * 4 + 1), (int) mDeploy->input_dim().Get(i * 4 + 2), (int) mDeploy->input_dim().Get(i * 4 + 3)}; } } ITensor* tensor = network.addInput(mDeploy->input().Get(i).c_str(), DataType::kFLOAT, dims); @@ -441,50 +441,10 @@ const IBlobNameToTensor* CaffeParser::parse(INetworkDefinition& network, } } } - - // If there is a pluginFactory provided, use layer name matching to handle the plugin construction - if (mPluginFactory && mPluginFactory->isPlugin(layerMsg.name().c_str())) - { - std::vector w = weights.getAllWeights(layerMsg.name()); - IPlugin* plugin = mPluginFactory->createPlugin(layerMsg.name().c_str(), w.empty() ? nullptr : &w[0], w.size()); - std::vector inputs; - for (int i = 0, n = layerMsg.bottom_size(); i < n; i++) - { - inputs.push_back((*mBlobNameToTensor)[layerMsg.bottom(i)]); - } - - bool isExt = mPluginFactoryIsExt && static_cast(mPluginFactory)->isPluginExt(layerMsg.name().c_str()); - - ILayer* layer = isExt ? network.addPluginExt(&inputs[0], int(inputs.size()), *static_cast(plugin)) - : network.addPlugin(&inputs[0], int(inputs.size()), *plugin); - - layer->setName(layerMsg.name().c_str()); - if (plugin->getNbOutputs() != layerMsg.top_size()) - { - std::cout << "Plugin layer output count is not equal to caffe output count" << std::endl; - ok = false; - } - for (int i = 0, n = std::min(layer->getNbOutputs(), layerMsg.top_size()); i < n; i++) - { - (*mBlobNameToTensor)[layerMsg.top(i)] = layer->getOutput(i); - } - - if (layer == nullptr) - { - std::cout << "error parsing layer type " << layerMsg.type() << " index " << i << std::endl; - ok = false; - } - - continue; - } if (getInferLibVersion() >= 5000) { if (mPluginFactoryV2 && mPluginFactoryV2->isPluginV2(layerMsg.name().c_str())) { - if (mPluginFactory) - { - RETURN_AND_LOG_ERROR(nullptr, "Both IPluginFactory and IPluginFactoryV2 are set. If using TensorRT 5.0 or later, switch to IPluginFactoryV2"); - } std::vector w = weights.getAllWeights(layerMsg.name()); nvinfer1::IPluginV2* plugin = mPluginFactoryV2->createPlugin(layerMsg.name().c_str(), w.empty() ? nullptr : &w[0], w.size(), mPluginNamespace.c_str()); std::vector inputs; @@ -596,14 +556,14 @@ const IBlobNameToTensor* CaffeParser::parse(INetworkDefinition& network, Dims d; if (network.hasImplicitBatchDimension()) { - d = DimsCHW{(int) shape.dim().Get(1), (int) shape.dim().Get(2), (int) shape.dim().Get(3)}; + d = Dims3{(int) shape.dim().Get(1), (int) shape.dim().Get(2), (int) shape.dim().Get(3)}; } else { std::cout << "Warning, setting batch size to 1. Update the dimension after parsing due to " "using explicit batch size." << std::endl; - d = DimsNCHW{1, (int) shape.dim().Get(1), (int) shape.dim().Get(2), (int) shape.dim().Get(3)}; + d = Dims4{1, (int) shape.dim().Get(1), (int) shape.dim().Get(2), (int) shape.dim().Get(3)}; } ITensor* tensor = network.addInput(layerMsg.top(i).c_str(), DataType::kFLOAT, d); (*mBlobNameToTensor)[layerMsg.top().Get(i)] = tensor; @@ -651,7 +611,7 @@ const IBlobNameToTensor* CaffeParser::parse(INetworkDefinition& network, return ok && weights.isOK() && mBlobNameToTensor->isOK() ? mBlobNameToTensor : nullptr; } -IBinaryProtoBlob* CaffeParser::parseBinaryProto(const char* fileName) +IBinaryProtoBlob* CaffeParser::parseBinaryProto(const char* fileName) noexcept { CHECK_NULL_RET_NULL(fileName) using namespace google::protobuf::io; @@ -675,7 +635,7 @@ IBinaryProtoBlob* CaffeParser::parseBinaryProto(const char* fileName) RETURN_AND_LOG_ERROR(nullptr, "parseBinaryProto: Could not parse mean file"); } - DimsNCHW dims{1, 1, 1, 1}; + Dims4 dims{1, 1, 1, 1}; if (blob.has_shape()) { int size = blob.shape().dim_size(), s[4] = {1, 1, 1, 1}; @@ -684,14 +644,14 @@ IBinaryProtoBlob* CaffeParser::parseBinaryProto(const char* fileName) assert(blob.shape().dim(i) < INT32_MAX); s[i] = static_cast(blob.shape().dim(i)); } - dims = DimsNCHW{s[0], s[1], s[2], s[3]}; + dims = Dims4{s[0], s[1], s[2], s[3]}; } else { - dims = DimsNCHW{blob.num(), blob.channels(), blob.height(), blob.width()}; + dims = Dims4{blob.num(), blob.channels(), blob.height(), blob.width()}; } - const int dataSize = dims.n() * dims.c() * dims.h() * dims.w(); + const int dataSize = parserutils::volume(dims); assert(dataSize > 0); const trtcaffe::Type blobProtoDataType = CaffeWeightFactory::getBlobProtoDataType(blob); diff --git a/parsers/caffe/caffeParser/caffeParser.h b/parsers/caffe/caffeParser/caffeParser.h index 9be2fa04..6dc861b4 100644 --- a/parsers/caffe/caffeParser/caffeParser.h +++ b/parsers/caffe/caffeParser/caffeParser.h @@ -35,32 +35,25 @@ public: const IBlobNameToTensor* parse(const char* deploy, const char* model, nvinfer1::INetworkDefinition& network, - nvinfer1::DataType weightType) override; + nvinfer1::DataType weightType) noexcept override; const IBlobNameToTensor* parseBuffers(const char* deployBuffer, size_t deployLength, const char* modelBuffer, size_t modelLength, nvinfer1::INetworkDefinition& network, - nvinfer1::DataType weightType) override; + nvinfer1::DataType weightType) noexcept override; - void setProtobufBufferSize(size_t size) override { mProtobufBufferSize = size; } - void setPluginFactory(nvcaffeparser1::IPluginFactory* factory) override { mPluginFactory = factory; } - void setPluginFactoryExt(nvcaffeparser1::IPluginFactoryExt* factory) override - { - mPluginFactory = factory; - mPluginFactoryIsExt = true; - } - - void setPluginFactoryV2(nvcaffeparser1::IPluginFactoryV2* factory) override { mPluginFactoryV2 = factory; } - void setPluginNamespace(const char* libNamespace) override { mPluginNamespace = libNamespace; } - IBinaryProtoBlob* parseBinaryProto(const char* fileName) override; - void destroy() override { delete this; } - void setErrorRecorder(nvinfer1::IErrorRecorder* recorder) override { (void)recorder; assert(!"TRT- Not implemented."); } - nvinfer1::IErrorRecorder* getErrorRecorder() const override { assert(!"TRT- Not implemented."); return nullptr; } + void setProtobufBufferSize(size_t size) noexcept override { mProtobufBufferSize = size; } + void setPluginFactoryV2(nvcaffeparser1::IPluginFactoryV2* factory) noexcept override { mPluginFactoryV2 = factory; } + void setPluginNamespace(const char* libNamespace) noexcept override { mPluginNamespace = libNamespace; } + IBinaryProtoBlob* parseBinaryProto(const char* fileName) noexcept override; + void destroy() noexcept override { delete this; } + void setErrorRecorder(nvinfer1::IErrorRecorder* recorder) noexcept override { (void)recorder; assert(!"TRT- Not implemented."); } + nvinfer1::IErrorRecorder* getErrorRecorder() const noexcept override { assert(!"TRT- Not implemented."); return nullptr; } private: - ~CaffeParser() override; + ~CaffeParser() noexcept override; std::vector parseNormalizeParam(const trtcaffe::LayerParameter& msg, CaffeWeightFactory& weightFactory, BlobNameToTensor& tensors); std::vector parsePriorBoxParam(const trtcaffe::LayerParameter& msg, CaffeWeightFactory& weightFactory, BlobNameToTensor& tensors); std::vector parseDetectionOutputParam(const trtcaffe::LayerParameter& msg, CaffeWeightFactory& weightFactory, BlobNameToTensor& tensors); @@ -84,7 +77,6 @@ private: std::vector mTmpAllocs; BlobNameToTensor* mBlobNameToTensor{nullptr}; size_t mProtobufBufferSize{INT_MAX}; - nvcaffeparser1::IPluginFactory* mPluginFactory{nullptr}; nvcaffeparser1::IPluginFactoryV2* mPluginFactoryV2{nullptr}; bool mPluginFactoryIsExt{false}; std::vector mNewPlugins; diff --git a/parsers/caffe/caffeParser/opParsers/parseBatchNorm.cpp b/parsers/caffe/caffeParser/opParsers/parseBatchNorm.cpp index d493d13f..6142be74 100644 --- a/parsers/caffe/caffeParser/opParsers/parseBatchNorm.cpp +++ b/parsers/caffe/caffeParser/opParsers/parseBatchNorm.cpp @@ -87,7 +87,7 @@ ILayer* parseBatchNormalization(INetworkDefinition& network, const trtcaffe::Lay const trtcaffe::BatchNormParameter& p = msg.batch_norm_param(); bool nvCaffe = weightFactory.getBlobsSize(msg.name()) == 5; - int C = parserutils::getCHW(tensors[msg.bottom(0)]->getDimensions()).c(); + int C = parserutils::getC(tensors[msg.bottom(0)]->getDimensions()); Weights mean{DataType::kFLOAT, nullptr, 0}, variance{DataType::kFLOAT, nullptr, 0}, diff --git a/parsers/caffe/caffeParser/opParsers/parseConv.cpp b/parsers/caffe/caffeParser/opParsers/parseConv.cpp index 4bf7db62..2d59234c 100644 --- a/parsers/caffe/caffeParser/opParsers/parseConv.cpp +++ b/parsers/caffe/caffeParser/opParsers/parseConv.cpp @@ -32,7 +32,7 @@ ILayer* parseConvolution(INetworkDefinition& network, const trtcaffe::LayerParam int kernelH = p.has_kernel_h() ? p.kernel_h() : p.kernel_size(0); int kernelW = p.has_kernel_w() ? p.kernel_w() : p.kernel_size_size() > 1 ? p.kernel_size(1) : p.kernel_size(0); - int C = parserutils::getCHW(tensors[msg.bottom(0)]->getDimensions()).c(); + int C = parserutils::getC(tensors[msg.bottom(0)]->getDimensions()); int G = p.has_group() ? p.group() : 1; auto CbyG = float(C / G * nbOutputs); diff --git a/parsers/caffe/caffeParser/opParsers/parseCrop.cpp b/parsers/caffe/caffeParser/opParsers/parseCrop.cpp index 8ba7efa2..4bbc1263 100644 --- a/parsers/caffe/caffeParser/opParsers/parseCrop.cpp +++ b/parsers/caffe/caffeParser/opParsers/parseCrop.cpp @@ -44,8 +44,8 @@ ILayer* parseCrop(INetworkDefinition& network, const trtcaffe::LayerParameter& m // ONLY IMPLEMENT SPATIAL CROPPING // IF CROP LAYER IS NOT SPATIAL CROP, ABORT const trtcaffe::CropParameter& p = msg.crop_param(); - DimsCHW inputDims = parserutils::getCHW(tensors[msg.bottom(0)]->getDimensions()); - DimsCHW refDims = parserutils::getCHW(tensors[msg.bottom(1)]->getDimensions()); + Dims3 inputDims = parserutils::getCHW(tensors[msg.bottom(0)]->getDimensions()); + Dims3 refDims = parserutils::getCHW(tensors[msg.bottom(1)]->getDimensions()); bool hasAxis = p.has_axis(); // optional parameter int axis = hasAxis ? p.axis() : 2; // default is 2 - spatial crop axis = (axis < 0) ? 4 + axis : axis; // axis negative number correction @@ -108,11 +108,11 @@ ILayer* parseCrop(INetworkDefinition& network, const trtcaffe::LayerParameter& m // - ( inputDims.w() - refDims.w() - offsetWidth ) = -inputDims.w() + refDims.w() + offsetWidth int prePadHeight = -offsetHeight; int prePadWidth = -offsetWidth; - int postPadHeight = -inputDims.h() + refDims.h() + offsetHeight; - int postPadWidth = -inputDims.w() + refDims.w() + offsetWidth; + int postPadHeight = -inputDims.d[1] + refDims.d[1] + offsetHeight; + int postPadWidth = -inputDims.d[2] + refDims.d[2] + offsetWidth; - DimsHW prePadding = DimsHW{prePadHeight, prePadWidth}; - DimsHW postPadding = DimsHW{postPadHeight, postPadWidth}; - return network.addPadding(*tensors[msg.bottom(0)], prePadding, postPadding); + Dims prePadding = parserutils::toDims(prePadHeight, prePadWidth); + Dims postPadding = parserutils::toDims(postPadHeight, postPadWidth); + return network.addPaddingNd(*tensors[msg.bottom(0)], prePadding, postPadding); } } //namespace nvcaffeparser1 diff --git a/parsers/caffe/caffeParser/opParsers/parseDeconv.cpp b/parsers/caffe/caffeParser/opParsers/parseDeconv.cpp index e3cf1767..00229c41 100644 --- a/parsers/caffe/caffeParser/opParsers/parseDeconv.cpp +++ b/parsers/caffe/caffeParser/opParsers/parseDeconv.cpp @@ -40,7 +40,7 @@ ILayer* parseDeconvolution(INetworkDefinition& network, const trtcaffe::LayerPar int kernelW = p.has_kernel_w() ? p.kernel_w() : p.kernel_size(0); int kernelH = p.has_kernel_h() ? p.kernel_h() : p.kernel_size_size() > 1 ? p.kernel_size(1) : p.kernel_size(0); - int C = parserutils::getCHW(tensors[msg.bottom(0)]->getDimensions()).c(); + int C = parserutils::getC(tensors[msg.bottom(0)]->getDimensions()); float std_dev = 1.0F / sqrtf(kernelW * kernelH * sqrtf(C * nbOutputs)); Weights kernelWeights = weightFactory.isInitialized() ? weightFactory(msg.name(), WeightType::kGENERIC) : weightFactory.allocateWeights(kernelW * kernelH * C * nbOutputs / nbGroups, std::normal_distribution(0.0F, std_dev)); diff --git a/parsers/caffe/caffeParser/opParsers/parsePReLU.cpp b/parsers/caffe/caffeParser/opParsers/parsePReLU.cpp index 48d58db3..f8ff0472 100644 --- a/parsers/caffe/caffeParser/opParsers/parsePReLU.cpp +++ b/parsers/caffe/caffeParser/opParsers/parsePReLU.cpp @@ -39,7 +39,7 @@ ILayer* parsePReLU(INetworkDefinition& network, const trtcaffe::LayerParameter& } int nWeights = channelShared ? 1 : inputDims.d[0]; // Caffe treats second input dimension as channels - Dims slopesDims{inputDims.nbDims, {}, {}}; + Dims slopesDims{inputDims.nbDims, {}}; std::fill(slopesDims.d, slopesDims.d + slopesDims.nbDims, 1); slopesDims.d[0] = nWeights; diff --git a/parsers/caffe/caffeParser/opParsers/parsePooling.cpp b/parsers/caffe/caffeParser/opParsers/parsePooling.cpp index 634ddd06..2f0325fa 100644 --- a/parsers/caffe/caffeParser/opParsers/parsePooling.cpp +++ b/parsers/caffe/caffeParser/opParsers/parsePooling.cpp @@ -37,9 +37,9 @@ ILayer* parsePooling(INetworkDefinition& network, const trtcaffe::LayerParameter int kernelH, kernelW; if (p.has_global_pooling() && p.global_pooling()) { - DimsCHW dims = parserutils::getCHW(tensors[msg.bottom(0)]->getDimensions()); - kernelH = dims.h(); - kernelW = dims.w(); + Dims3 dims = parserutils::getCHW(tensors[msg.bottom(0)]->getDimensions()); + kernelH = dims.d[1]; + kernelW = dims.d[2]; } else { diff --git a/parsers/caffe/caffeParser/opParsers/parseScale.cpp b/parsers/caffe/caffeParser/opParsers/parseScale.cpp index b68ce689..3eefdb8b 100644 --- a/parsers/caffe/caffeParser/opParsers/parseScale.cpp +++ b/parsers/caffe/caffeParser/opParsers/parseScale.cpp @@ -28,7 +28,7 @@ ILayer* parseScale(INetworkDefinition& network, const trtcaffe::LayerParameter& } const trtcaffe::ScaleParameter& p = msg.scale_param(); - int C = parserutils::getCHW(tensors[msg.bottom(0)]->getDimensions()).c(); + int C = parserutils::getC(tensors[msg.bottom(0)]->getDimensions()); Weights scale = weightFactory.isInitialized() ? weightFactory(msg.name(), WeightType::kGENERIC) : weightFactory.allocateWeights(C, std::uniform_real_distribution(0.9F, 1.1F)); Weights shift = !p.has_bias_term() || p.bias_term() ? (weightFactory.isInitialized() ? weightFactory(msg.name(), WeightType::kBIAS) : weightFactory.allocateWeights(C)) : weightFactory.getNullWeights(); diff --git a/parsers/common/parserUtils.h b/parsers/common/parserUtils.h index c82478c1..8c03fbd9 100644 --- a/parsers/common/parserUtils.h +++ b/parsers/common/parserUtils.h @@ -101,26 +101,25 @@ inline std::ostream& operator<<(std::ostream& o, nvinfer1::DataType dt) case nvinfer1::DataType::kFLOAT: o << "Float"; break; case nvinfer1::DataType::kHALF: o << "Half"; break; case nvinfer1::DataType::kINT8: o << "Int8"; break; + case nvinfer1::DataType::kBOOL: o << "Bool"; break; } return o; } -inline nvinfer1::DimsCHW getCHW(const nvinfer1::Dims& d) +inline nvinfer1::Dims3 getCHW(const nvinfer1::Dims& d) { assert(d.nbDims >= 3); - return nvinfer1::DimsCHW(d.d[d.nbDims - 3], d.d[d.nbDims - 2], d.d[d.nbDims - 1]); + return nvinfer1::Dims3(d.d[d.nbDims - 3], d.d[d.nbDims - 2], d.d[d.nbDims - 1]); } -inline nvinfer1::DimsCHW getCHWWithExpansion(const nvinfer1::Dims& d, int filler) +inline int32_t getC(const nvinfer1::Dims& d) { - if (d.nbDims == 0) - return nvinfer1::DimsCHW(filler, filler, filler); - else if (d.nbDims == 1) - return nvinfer1::DimsCHW(filler, filler, d.d[0]); - else if (d.nbDims == 2) - return nvinfer1::DimsCHW(filler, d.d[0], d.d[1]); - else - return nvinfer1::DimsCHW(d.d[d.nbDims - 3], d.d[d.nbDims - 2], d.d[d.nbDims - 1]); + return getCHW(d).d[0]; +} + +inline nvinfer1::Dims toDims(int32_t w, int32_t h) noexcept +{ + return nvinfer1::Dims{2, {w, h}}; } inline int combineIndexDimensions(int batchSize, const nvinfer1::Dims& d) diff --git a/plugin/CMakeLists.txt b/plugin/CMakeLists.txt index 806c3ade..f29ed5dd 100644 --- a/plugin/CMakeLists.txt +++ b/plugin/CMakeLists.txt @@ -35,6 +35,7 @@ set(PLUGIN_LISTS coordConvACPlugin cropAndResizePlugin detectionLayerPlugin + efficientNMSPlugin flattenConcat generateDetectionPlugin gridAnchorPlugin @@ -53,6 +54,7 @@ set(PLUGIN_LISTS regionPlugin reorgPlugin resizeNearestPlugin + scatterPlugin specialSlicePlugin splitPlugin ) @@ -70,7 +72,7 @@ if(BERT_GENCODES) ) endif() -include_directories(common common/kernels ../samples/common) +include_directories(common common/kernels ${CMAKE_SOURCE_DIR}/samples/common) foreach(PLUGIN_ITER ${PLUGIN_LISTS}) include_directories(${PLUGIN_ITER}) diff --git a/plugin/InferPlugin.cpp b/plugin/InferPlugin.cpp index 0d97f439..5fe39c5b 100644 --- a/plugin/InferPlugin.cpp +++ b/plugin/InferPlugin.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "NvInfer.h" #include "NvInferPlugin.h" #include "checkMacrosPlugin.h" @@ -33,6 +32,7 @@ using namespace nvinfer1::plugin; #include "coordConvACPlugin.h" #include "cropAndResizePlugin.h" #include "detectionLayerPlugin.h" +#include "efficientNMSPlugin.h" #include "flattenConcat.h" #include "generateDetectionPlugin.h" #include "gridAnchorPlugin.h" @@ -53,6 +53,7 @@ using namespace nvinfer1::plugin; #include "resizeNearestPlugin.h" #include "specialSlicePlugin.h" #include "split.h" +#include "scatterPlugin.h" using nvinfer1::plugin::RPROIParams; @@ -169,6 +170,8 @@ extern "C" initializePlugin(logger, libNamespace); initializePlugin(logger, libNamespace); initializePlugin(logger, libNamespace); + initializePlugin(logger, libNamespace); + initializePlugin(logger, libNamespace); initializePlugin(logger, libNamespace); initializePlugin(logger, libNamespace); initializePlugin(logger, libNamespace); @@ -189,6 +192,7 @@ extern "C" initializePlugin(logger, libNamespace); initializePlugin(logger, libNamespace); initializePlugin(logger, libNamespace); + initializePlugin(logger, libNamespace); initializePlugin(logger, libNamespace); initializePlugin(logger, libNamespace); return true; diff --git a/plugin/README.md b/plugin/README.md index beb66149..76742b03 100644 --- a/plugin/README.md +++ b/plugin/README.md @@ -11,6 +11,8 @@ | [coordConvACPlugin](coordConvACPlugin) | CoordConvAC | 1 | | [cropAndResizePlugin](cropAndResizePlugin) | CropAndResize | 1 | | [detectionLayerPlugin](detectionLayerPlugin) | DetectionLayer_TRT | 1 | +| [efficientNMSPlugin](efficientNMSPlugin) | EfficientNMS_TRT | 1 | +| [efficientNMSONNXPlugin](efficientNMSPlugin) | EfficientNMS_ONNX_TRT | 1 | | [embLayerNormPlugin](embLayerNormPlugin) | CustomEmbLayerNormPluginDynamic | 1, 2 | | [fcPlugin](fcPlugin) | CustomFCPluginDynamic | 1 | | [flattenConcat](flattenConcat) | FlattenConcat_TRT | 1 | @@ -33,6 +35,7 @@ | [regionPlugin](regionPlugin) | Region_TRT | 1 | | [reorgPlugin](reorgPlugin) | Reorg_TRT | 1 | | [resizeNearestPlugin](resizeNearestPlugin) | ResizeNearest_TRT | 1 | +| [scatterPlugin](scatterPlugin) | ScatterND | 1 | | [skipLayerNormPlugin](skipLayerNormPlugin) | CustomSkipLayerNormPluginDynamic | 1, 2, 3 | | [specialSlicePlugin](specialSlicePlugin) | SpecialSlice_TRT | 1 | | [splitPlugin](splitPlugin) | Split | 1 | diff --git a/plugin/batchTilePlugin/batchTilePlugin.cpp b/plugin/batchTilePlugin/batchTilePlugin.cpp index 6f3db20c..a28dc36c 100644 --- a/plugin/batchTilePlugin/batchTilePlugin.cpp +++ b/plugin/batchTilePlugin/batchTilePlugin.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "batchTilePlugin.h" #include #include @@ -65,78 +64,94 @@ BatchTilePlugin::BatchTilePlugin(const std::string name, const void* data, size_ assert(d == a + length); } -int BatchTilePlugin::getNbOutputs() const +int BatchTilePlugin::getNbOutputs() const noexcept { return 1; } -Dims BatchTilePlugin::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) +Dims BatchTilePlugin::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept { - assert(nbInputDims == 2); - assert(index == 0); - assert(inputs[1].nbDims == 4); - return DimsCHW(inputs[1].d[1], inputs[1].d[2], inputs[1].d[3]); + try + { + assert(nbInputDims == 2); + assert(index == 0); + assert(inputs[1].nbDims == 4); + return Dims3(inputs[1].d[1], inputs[1].d[2], inputs[1].d[3]); + } + catch (const std::exception& e) + { + caughtError(e); + } + return Dims{}; } -int BatchTilePlugin::initialize() +int BatchTilePlugin::initialize() noexcept { return STATUS_SUCCESS; } -size_t BatchTilePlugin::getWorkspaceSize(int) const +size_t BatchTilePlugin::getWorkspaceSize(int) const noexcept { return 0; } -DataType BatchTilePlugin::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const +DataType BatchTilePlugin::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { assert(index == 0); return DataType::kFLOAT; } -int BatchTilePlugin::enqueue(int batchSize, const void* const* inputs, void** outputs, void*, cudaStream_t stream) +int BatchTilePlugin::enqueue( + int batchSize, const void* const* inputs, void* const* outputs, void*, cudaStream_t stream) noexcept { - char* output = reinterpret_cast(outputs[0]); - // expand to batch size - for (int i = 0; i < batchSize; i++) + try { - auto ret = cudaMemcpyAsync(output + i * mCopySize, inputs[1], mCopySize, cudaMemcpyDeviceToDevice, stream); - if (ret != 0) + char* output = reinterpret_cast(outputs[0]); + // expand to batch size + for (int i = 0; i < batchSize; i++) { - std::cout << "Cuda failure: " << ret; - abort(); + auto ret = cudaMemcpyAsync(output + i * mCopySize, inputs[1], mCopySize, cudaMemcpyDeviceToDevice, stream); + if (ret != cudaSuccess) + { + return ret; + } } + return STATUS_SUCCESS; } - return 0; + catch (const std::exception& e) + { + caughtError(e); + } + return -1; } -void BatchTilePlugin::serialize(void* buffer) const +void BatchTilePlugin::serialize(void* buffer) const noexcept { char *d = reinterpret_cast(buffer), *a = d; writeToBuffer(d, mCopySize); assert(d == a + getSerializationSize()); } -void BatchTilePlugin::terminate() {} +void BatchTilePlugin::terminate() noexcept {} -size_t BatchTilePlugin::getSerializationSize() const +size_t BatchTilePlugin::getSerializationSize() const noexcept { return sizeof(size_t); } -bool BatchTilePlugin::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const +bool BatchTilePlugin::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept { return false; } -bool BatchTilePlugin::canBroadcastInputAcrossBatch(int inputIndex) const +bool BatchTilePlugin::canBroadcastInputAcrossBatch(int inputIndex) const noexcept { return false; } void BatchTilePlugin::configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept { assert(nbOutputs == 1); assert(inputDims[1].nbDims == 4); @@ -144,38 +159,46 @@ void BatchTilePlugin::configurePlugin(const Dims* inputDims, int nbInputs, const mCopySize = std::accumulate(inputDims[1].d, inputDims[1].d + 4, 1, std::multiplies()) * sizeof(float); } -bool BatchTilePlugin::supportsFormat(DataType type, PluginFormat format) const +bool BatchTilePlugin::supportsFormat(DataType type, PluginFormat format) const noexcept { - return (type == DataType::kFLOAT && format == PluginFormat::kNCHW); + return (type == DataType::kFLOAT && format == PluginFormat::kLINEAR); } -const char* BatchTilePlugin::getPluginType() const +const char* BatchTilePlugin::getPluginType() const noexcept { return BATCH_TILE_PLUGIN_NAME; } -const char* BatchTilePlugin::getPluginVersion() const +const char* BatchTilePlugin::getPluginVersion() const noexcept { return BATCH_TILE_PLUGIN_VERSION; } -void BatchTilePlugin::destroy() +void BatchTilePlugin::destroy() noexcept { delete this; } -IPluginV2Ext* BatchTilePlugin::clone() const +IPluginV2Ext* BatchTilePlugin::clone() const noexcept { - auto* plugin = new BatchTilePlugin(mLayerName, mCopySize); - plugin->setPluginNamespace(mNamespace.c_str()); - return plugin; + try + { + auto* plugin = new BatchTilePlugin(mLayerName, mCopySize); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } -void BatchTilePlugin::setPluginNamespace(const char* libNamespace) +void BatchTilePlugin::setPluginNamespace(const char* libNamespace) noexcept { mNamespace = libNamespace; } -const char* BatchTilePlugin::getPluginNamespace() const +const char* BatchTilePlugin::getPluginNamespace() const noexcept { return mNamespace.c_str(); } @@ -186,29 +209,58 @@ BatchTilePluginCreator::BatchTilePluginCreator() mFC.fields = nullptr; } -const char* BatchTilePluginCreator::getPluginName() const +const char* BatchTilePluginCreator::getPluginName() const noexcept { return BATCH_TILE_PLUGIN_NAME; } -const char* BatchTilePluginCreator::getPluginVersion() const +const char* BatchTilePluginCreator::getPluginVersion() const noexcept { return BATCH_TILE_PLUGIN_VERSION; } -const PluginFieldCollection* BatchTilePluginCreator::getFieldNames() +const PluginFieldCollection* BatchTilePluginCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2Ext* BatchTilePluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2Ext* BatchTilePluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { - auto* plugin = new BatchTilePlugin(name); - plugin->setPluginNamespace(mNamespace.c_str()); - return plugin; + try + { + auto* plugin = new BatchTilePlugin(name); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } -IPluginV2Ext* BatchTilePluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) +void BatchTilePluginCreator::setPluginNamespace(const char* libNamespace) noexcept { - return new BatchTilePlugin(name, serialData, serialLength); + try + { + mNamespace = libNamespace; + } + catch (const std::exception& e) + { + caughtError(e); + } +} + +IPluginV2Ext* BatchTilePluginCreator::deserializePlugin( + const char* name, const void* serialData, size_t serialLength) noexcept +{ + try + { + return new BatchTilePlugin(name, serialData, serialLength); + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } diff --git a/plugin/batchTilePlugin/batchTilePlugin.h b/plugin/batchTilePlugin/batchTilePlugin.h index 3464f523..d8f9ad6b 100644 --- a/plugin/batchTilePlugin/batchTilePlugin.h +++ b/plugin/batchTilePlugin/batchTilePlugin.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef BATCHTILEPLUGIN_H #define BATCHTILEPLUGIN_H #include "NvInferPlugin.h" @@ -35,45 +34,45 @@ public: BatchTilePlugin() = delete; - int getNbOutputs() const override; + int getNbOutputs() const noexcept override; - Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) override; + Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept override; - int initialize() override; - void terminate() override; + int initialize() noexcept override; + void terminate() noexcept override; - size_t getWorkspaceSize(int) const override; + size_t getWorkspaceSize(int) const noexcept override; - int enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) override; + int enqueue(int batchSize, const void* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept override; - DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept override; - size_t getSerializationSize() const override; + size_t getSerializationSize() const noexcept override; - void serialize(void* buffer) const override; + void serialize(void* buffer) const noexcept override; - bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const override; + bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept override; - bool canBroadcastInputAcrossBatch(int inputIndex) const override; + bool canBroadcastInputAcrossBatch(int inputIndex) const noexcept override; void configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) override; + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept override; - bool supportsFormat(DataType type, PluginFormat format) const override; + bool supportsFormat(DataType type, PluginFormat format) const noexcept override; - const char* getPluginType() const override; + const char* getPluginType() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - void destroy() override; + void destroy() noexcept override; - IPluginV2Ext* clone() const override; + IPluginV2Ext* clone() const noexcept override; - void setPluginNamespace(const char* libNamespace) override; + void setPluginNamespace(const char* libNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; private: const std::string mLayerName; @@ -86,22 +85,19 @@ class BatchTilePluginCreator : public BaseCreator public: BatchTilePluginCreator(); - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const PluginFieldCollection* getFieldNames() override; + const PluginFieldCollection* getFieldNames() noexcept override; - IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) override; + IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; - IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; - void setPluginNamespace(const char* libNamespace) override - { - mNamespace = libNamespace; - } + void setPluginNamespace(const char* libNamespace) noexcept override; - const char* getPluginNamespace() const override + const char* getPluginNamespace() const noexcept override { return mNamespace.c_str(); } diff --git a/plugin/batchedNMSPlugin/batchedNMSPlugin.cpp b/plugin/batchedNMSPlugin/batchedNMSPlugin.cpp index d1074153..90ccf23f 100644 --- a/plugin/batchedNMSPlugin/batchedNMSPlugin.cpp +++ b/plugin/batchedNMSPlugin/batchedNMSPlugin.cpp @@ -35,15 +35,50 @@ const char* NMS_PLUGIN_VERSION{"1"}; const char* NMS_PLUGIN_NAMES[] = {"BatchedNMS_TRT", "BatchedNMSDynamic_TRT"}; } // namespace +namespace nvinfer1 +{ +namespace plugin +{ +template <> +void write(char*& buffer, const NMSParameters& val) +{ + auto* param = reinterpret_cast(buffer); + param->shareLocation = val.shareLocation; + param->backgroundLabelId = val.backgroundLabelId; + param->numClasses = val.numClasses; + param->topK = val.topK; + param->keepTopK = val.keepTopK; + param->scoreThreshold = val.scoreThreshold; + param->iouThreshold = val.iouThreshold; + param->isNormalized = val.isNormalized; + buffer += sizeof(NMSParameters); +} +} // namespace plugin +} // namespace nvinfer1 + PluginFieldCollection BatchedNMSBasePluginCreator::mFC{}; std::vector BatchedNMSBasePluginCreator::mPluginAttributes; -BatchedNMSPlugin::BatchedNMSPlugin(NMSParameters params) noexcept - : param(params) +static inline pluginStatus_t checkParams(const NMSParameters& param) { + // NMS plugin supports maximum thread blocksize of 512 and upto 8 blocks at once. + constexpr int32_t maxTopK{512*8}; + if (param.topK > maxTopK) + { + gLogError << "Invalid parameter: NMS topK (" << param.topK << ") exceeds limit (" << maxTopK << ")" << std::endl; + return STATUS_BAD_PARAM; + } + + return STATUS_SUCCESS; } -BatchedNMSPlugin::BatchedNMSPlugin(const void* data, size_t length) noexcept +BatchedNMSPlugin::BatchedNMSPlugin(NMSParameters params) + : param(params) +{ + mPluginStatus = checkParams(param); +} + +BatchedNMSPlugin::BatchedNMSPlugin(const void* data, size_t length) { const char *d = reinterpret_cast(data), *a = d; param = read(d); @@ -54,14 +89,17 @@ BatchedNMSPlugin::BatchedNMSPlugin(const void* data, size_t length) noexcept mPrecision = read(d); mScoreBits = read(d); ASSERT(d == a + length); + + mPluginStatus = checkParams(param); } -BatchedNMSDynamicPlugin::BatchedNMSDynamicPlugin(NMSParameters params) noexcept +BatchedNMSDynamicPlugin::BatchedNMSDynamicPlugin(NMSParameters params) : param(params) { + mPluginStatus = checkParams(param); } -BatchedNMSDynamicPlugin::BatchedNMSDynamicPlugin(const void* data, size_t length) noexcept +BatchedNMSDynamicPlugin::BatchedNMSDynamicPlugin(const void* data, size_t length) { const char *d = reinterpret_cast(data), *a = d; param = read(d); @@ -72,6 +110,8 @@ BatchedNMSDynamicPlugin::BatchedNMSDynamicPlugin(const void* data, size_t length mPrecision = read(d); mScoreBits = read(d); ASSERT(d == a + length); + + mPluginStatus = checkParams(param); } int BatchedNMSPlugin::getNbOutputs() const noexcept @@ -100,100 +140,116 @@ void BatchedNMSDynamicPlugin::terminate() noexcept {} Dims BatchedNMSPlugin::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept { - ASSERT(nbInputDims == 2); - ASSERT(index >= 0 && index < this->getNbOutputs()); - ASSERT(inputs[0].nbDims == 3); - ASSERT(inputs[1].nbDims == 2 || (inputs[1].nbDims == 3 && inputs[1].d[2] == 1)); - // boxesSize: number of box coordinates for one sample - boxesSize = inputs[0].d[0] * inputs[0].d[1] * inputs[0].d[2]; - // scoresSize: number of scores for one sample - scoresSize = inputs[1].d[0] * inputs[1].d[1]; - // num_detections - if (index == 0) + try { - Dims dim0{}; - dim0.nbDims = 0; - return dim0; + ASSERT(nbInputDims == 2); + ASSERT(index >= 0 && index < this->getNbOutputs()); + ASSERT(inputs[0].nbDims == 3); + ASSERT(inputs[1].nbDims == 2 || (inputs[1].nbDims == 3 && inputs[1].d[2] == 1)); + // boxesSize: number of box coordinates for one sample + boxesSize = inputs[0].d[0] * inputs[0].d[1] * inputs[0].d[2]; + // scoresSize: number of scores for one sample + scoresSize = inputs[1].d[0] * inputs[1].d[1]; + // num_detections + if (index == 0) + { + Dims dim0{}; + dim0.nbDims = 0; + return dim0; + } + // nmsed_boxes + if (index == 1) + { + return DimsHW(param.keepTopK, 4); + } + // nmsed_scores or nmsed_classes + Dims dim1{}; + dim1.nbDims = 1; + dim1.d[0] = param.keepTopK; + return dim1; } - // nmsed_boxes - if (index == 1) + catch (const std::exception& e) { - return DimsHW(param.keepTopK, 4); + caughtError(e); } - // nmsed_scores or nmsed_classes - Dims dim1{}; - dim1.nbDims = 1; - dim1.d[0] = param.keepTopK; - return dim1; + return Dims{}; } DimsExprs BatchedNMSDynamicPlugin::getOutputDimensions( int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) noexcept { - ASSERT(nbInputs == 2); - ASSERT(outputIndex >= 0 && outputIndex < this->getNbOutputs()); - - // Shape of boxes input should be - // Constant shape: [batch_size, num_boxes, num_classes, 4] or [batch_size, num_boxes, 1, 4] - // shareLocation == 0 or 1 - // or - // Dynamic shape: some dimension values may be -1 - ASSERT(inputs[0].nbDims == 4); - - // Shape of scores input should be - // Constant shape: [batch_size, num_boxes, num_classes] or [batch_size, num_boxes, num_classes, 1] - // or - // Dynamic shape: some dimension values may be -1 - ASSERT(inputs[1].nbDims == 3 || inputs[1].nbDims == 4); - - if (inputs[0].d[0]->isConstant() && inputs[0].d[1]->isConstant() && inputs[0].d[2]->isConstant() - && inputs[0].d[3]->isConstant()) + try { - boxesSize = exprBuilder - .operation(DimensionOperation::kPROD, - *exprBuilder.operation(DimensionOperation::kPROD, *inputs[0].d[1], *inputs[0].d[2]), - *inputs[0].d[3]) - ->getConstantValue(); - } + ASSERT(nbInputs == 2); + ASSERT(outputIndex >= 0 && outputIndex < this->getNbOutputs()); - if (inputs[1].d[0]->isConstant() && inputs[1].d[1]->isConstant() && inputs[1].d[2]->isConstant()) - { - scoresSize - = exprBuilder.operation(DimensionOperation::kPROD, *inputs[1].d[1], *inputs[1].d[2])->getConstantValue(); - } + // Shape of boxes input should be + // Constant shape: [batch_size, num_boxes, num_classes, 4] or [batch_size, num_boxes, 1, 4] + // shareLocation == 0 or 1 + // or + // Dynamic shape: some dimension values may be -1 + ASSERT(inputs[0].nbDims == 4); - DimsExprs out_dim; - // num_detections - if (outputIndex == 0) - { - out_dim.nbDims = 2; - out_dim.d[0] = inputs[0].d[0]; - out_dim.d[1] = exprBuilder.constant(1); - } - // nmsed_boxes - else if (outputIndex == 1) - { - out_dim.nbDims = 3; - out_dim.d[0] = inputs[0].d[0]; - out_dim.d[1] = exprBuilder.constant(param.keepTopK); - out_dim.d[2] = exprBuilder.constant(4); - } - // nmsed_scores - else if (outputIndex == 2) - { - out_dim.nbDims = 2; - out_dim.d[0] = inputs[0].d[0]; - out_dim.d[1] = exprBuilder.constant(param.keepTopK); - } - // nmsed_classes - else - { - out_dim.nbDims = 2; - out_dim.d[0] = inputs[0].d[0]; - out_dim.d[1] = exprBuilder.constant(param.keepTopK); - } + // Shape of scores input should be + // Constant shape: [batch_size, num_boxes, num_classes] or [batch_size, num_boxes, num_classes, 1] + // or + // Dynamic shape: some dimension values may be -1 + ASSERT(inputs[1].nbDims == 3 || inputs[1].nbDims == 4); - return out_dim; + if (inputs[0].d[0]->isConstant() && inputs[0].d[1]->isConstant() && inputs[0].d[2]->isConstant() + && inputs[0].d[3]->isConstant()) + { + boxesSize = exprBuilder + .operation(DimensionOperation::kPROD, + *exprBuilder.operation(DimensionOperation::kPROD, *inputs[0].d[1], *inputs[0].d[2]), + *inputs[0].d[3]) + ->getConstantValue(); + } + + if (inputs[1].d[0]->isConstant() && inputs[1].d[1]->isConstant() && inputs[1].d[2]->isConstant()) + { + scoresSize = exprBuilder.operation(DimensionOperation::kPROD, *inputs[1].d[1], *inputs[1].d[2]) + ->getConstantValue(); + } + + DimsExprs out_dim; + // num_detections + if (outputIndex == 0) + { + out_dim.nbDims = 2; + out_dim.d[0] = inputs[0].d[0]; + out_dim.d[1] = exprBuilder.constant(1); + } + // nmsed_boxes + else if (outputIndex == 1) + { + out_dim.nbDims = 3; + out_dim.d[0] = inputs[0].d[0]; + out_dim.d[1] = exprBuilder.constant(param.keepTopK); + out_dim.d[2] = exprBuilder.constant(4); + } + // nmsed_scores + else if (outputIndex == 2) + { + out_dim.nbDims = 2; + out_dim.d[0] = inputs[0].d[0]; + out_dim.d[1] = exprBuilder.constant(param.keepTopK); + } + // nmsed_classes + else + { + out_dim.nbDims = 2; + out_dim.d[0] = inputs[0].d[0]; + out_dim.d[1] = exprBuilder.constant(param.keepTopK); + } + + return out_dim; + } + catch (const std::exception& e) + { + caughtError(e); + } + return DimsExprs{}; } size_t BatchedNMSPlugin::getWorkspaceSize(int maxBatchSize) const noexcept @@ -210,41 +266,66 @@ size_t BatchedNMSDynamicPlugin::getWorkspaceSize( } int BatchedNMSPlugin::enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) noexcept + int32_t batchSize, void const* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept { - const void* const locData = inputs[0]; - const void* const confData = inputs[1]; + try + { + const void* const locData = inputs[0]; + const void* const confData = inputs[1]; - void* keepCount = outputs[0]; - void* nmsedBoxes = outputs[1]; - void* nmsedScores = outputs[2]; - void* nmsedClasses = outputs[3]; + if (mPluginStatus != STATUS_SUCCESS) + { + return -1; + } - pluginStatus_t status = nmsInference(stream, batchSize, boxesSize, scoresSize, param.shareLocation, - param.backgroundLabelId, numPriors, param.numClasses, param.topK, param.keepTopK, param.scoreThreshold, - param.iouThreshold, mPrecision, locData, mPrecision, confData, keepCount, nmsedBoxes, nmsedScores, nmsedClasses, - workspace, param.isNormalized, false, mClipBoxes, mScoreBits); - ASSERT(status == STATUS_SUCCESS); - return 0; + void* keepCount = outputs[0]; + void* nmsedBoxes = outputs[1]; + void* nmsedScores = outputs[2]; + void* nmsedClasses = outputs[3]; + + pluginStatus_t status = nmsInference(stream, batchSize, boxesSize, scoresSize, param.shareLocation, + param.backgroundLabelId, numPriors, param.numClasses, param.topK, param.keepTopK, param.scoreThreshold, + param.iouThreshold, mPrecision, locData, mPrecision, confData, keepCount, nmsedBoxes, nmsedScores, nmsedClasses, + workspace, param.isNormalized, false, mClipBoxes, mScoreBits); + return status == STATUS_SUCCESS ? 0 : -1; + } + catch (const std::exception& e) + { + caughtError(e); + } + return -1; } int BatchedNMSDynamicPlugin::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { - const void* const locData = inputs[0]; - const void* const confData = inputs[1]; + try + { + const void* const locData = inputs[0]; + const void* const confData = inputs[1]; - void* keepCount = outputs[0]; - void* nmsedBoxes = outputs[1]; - void* nmsedScores = outputs[2]; - void* nmsedClasses = outputs[3]; + if (mPluginStatus != STATUS_SUCCESS) + { + return -1; + } - pluginStatus_t status = nmsInference(stream, inputDesc[0].dims.d[0], boxesSize, scoresSize, param.shareLocation, - param.backgroundLabelId, numPriors, param.numClasses, param.topK, param.keepTopK, param.scoreThreshold, - param.iouThreshold, mPrecision, locData, mPrecision, confData, keepCount, nmsedBoxes, nmsedScores, nmsedClasses, - workspace, param.isNormalized, false, mClipBoxes, mScoreBits); - ASSERT(status == STATUS_SUCCESS); - return 0; + void* keepCount = outputs[0]; + void* nmsedBoxes = outputs[1]; + void* nmsedScores = outputs[2]; + void* nmsedClasses = outputs[3]; + + pluginStatus_t status = nmsInference(stream, inputDesc[0].dims.d[0], boxesSize, scoresSize, param.shareLocation, + param.backgroundLabelId, numPriors, param.numClasses, param.topK, param.keepTopK, param.scoreThreshold, + param.iouThreshold, mPrecision, locData, mPrecision, confData, keepCount, nmsedBoxes, nmsedScores, nmsedClasses, + workspace, param.isNormalized, false, mClipBoxes, mScoreBits); + return status; + } + catch (const std::exception& e) + { + caughtError(e); + } + return -1; } size_t BatchedNMSPlugin::getSerializationSize() const noexcept @@ -289,60 +370,76 @@ void BatchedNMSPlugin::configurePlugin(const Dims* inputDims, int nbInputs, cons const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, const bool* outputIsBroadcast, nvinfer1::PluginFormat format, int maxBatchSize) noexcept { - ASSERT(nbInputs == 2); - ASSERT(nbOutputs == 4); - ASSERT(inputDims[0].nbDims == 3); - ASSERT(inputDims[1].nbDims == 2 || (inputDims[1].nbDims == 3 && inputDims[1].d[2] == 1)); - ASSERT(std::none_of(inputIsBroadcast, inputIsBroadcast + nbInputs, [](bool b) { return b; })); - ASSERT(std::none_of(outputIsBroadcast, outputIsBroadcast + nbInputs, [](bool b) { return b; })); + try + { + ASSERT(nbInputs == 2); + ASSERT(nbOutputs == 4); + ASSERT(inputDims[0].nbDims == 3); + ASSERT(inputDims[1].nbDims == 2 || (inputDims[1].nbDims == 3 && inputDims[1].d[2] == 1)); + ASSERT(std::none_of(inputIsBroadcast, inputIsBroadcast + nbInputs, [](bool b) { return b; })); + ASSERT(std::none_of(outputIsBroadcast, outputIsBroadcast + nbInputs, [](bool b) { return b; })); - boxesSize = inputDims[0].d[0] * inputDims[0].d[1] * inputDims[0].d[2]; - scoresSize = inputDims[1].d[0] * inputDims[1].d[1]; - // num_boxes - numPriors = inputDims[0].d[0]; - const int numLocClasses = param.shareLocation ? 1 : param.numClasses; - // Third dimension of boxes must be either 1 or num_classes - ASSERT(inputDims[0].d[1] == numLocClasses); - ASSERT(inputDims[0].d[2] == 4); - mPrecision = inputTypes[0]; + boxesSize = inputDims[0].d[0] * inputDims[0].d[1] * inputDims[0].d[2]; + scoresSize = inputDims[1].d[0] * inputDims[1].d[1]; + // num_boxes + numPriors = inputDims[0].d[0]; + const int numLocClasses = param.shareLocation ? 1 : param.numClasses; + // Third dimension of boxes must be either 1 or num_classes + ASSERT(inputDims[0].d[1] == numLocClasses); + ASSERT(inputDims[0].d[2] == 4); + mPrecision = inputTypes[0]; + } + catch (const std::exception& e) + { + caughtError(e); + } } void BatchedNMSDynamicPlugin::configurePlugin( const DynamicPluginTensorDesc* in, int nbInputs, const DynamicPluginTensorDesc* out, int nbOutputs) noexcept { - ASSERT(nbInputs == 2); - ASSERT(nbOutputs == 4); + try + { + ASSERT(nbInputs == 2); + ASSERT(nbOutputs == 4); - // Shape of boxes input should be - // Constant shape: [batch_size, num_boxes, num_classes, 4] or [batch_size, num_boxes, 1, 4] - // shareLocation == 0 or 1 - const int numLocClasses = param.shareLocation ? 1 : param.numClasses; - ASSERT(in[0].desc.dims.nbDims == 4); - ASSERT(in[0].desc.dims.d[2] == numLocClasses); - ASSERT(in[0].desc.dims.d[3] == 4); + // Shape of boxes input should be + // Constant shape: [batch_size, num_boxes, num_classes, 4] or [batch_size, num_boxes, 1, 4] + // shareLocation == 0 or 1 + const int numLocClasses = param.shareLocation ? 1 : param.numClasses; + ASSERT(in[0].desc.dims.nbDims == 4); + ASSERT(in[0].desc.dims.d[2] == numLocClasses); + ASSERT(in[0].desc.dims.d[3] == 4); - // Shape of scores input should be - // Constant shape: [batch_size, num_boxes, num_classes] or [batch_size, num_boxes, num_classes, 1] - ASSERT(in[1].desc.dims.nbDims == 3 || (in[1].desc.dims.nbDims == 4 && in[1].desc.dims.d[3] == 1)); + // Shape of scores input should be + // Constant shape: [batch_size, num_boxes, num_classes] or [batch_size, num_boxes, num_classes, 1] + ASSERT(in[1].desc.dims.nbDims == 3 || (in[1].desc.dims.nbDims == 4 && in[1].desc.dims.d[3] == 1)); - boxesSize = in[0].desc.dims.d[1] * in[0].desc.dims.d[2] * in[0].desc.dims.d[3]; - scoresSize = in[1].desc.dims.d[1] * in[1].desc.dims.d[2]; - // num_boxes - numPriors = in[0].desc.dims.d[1]; + boxesSize = in[0].desc.dims.d[1] * in[0].desc.dims.d[2] * in[0].desc.dims.d[3]; + scoresSize = in[1].desc.dims.d[1] * in[1].desc.dims.d[2]; + // num_boxes + numPriors = in[0].desc.dims.d[1]; - mPrecision = in[0].desc.type; + mPrecision = in[0].desc.type; + } + catch (const std::exception& e) + { + caughtError(e); + } } bool BatchedNMSPlugin::supportsFormat(DataType type, PluginFormat format) const noexcept { return ((type == DataType::kHALF || type == DataType::kFLOAT || type == DataType::kINT32) - && format == PluginFormat::kNCHW); + && format == PluginFormat::kLINEAR); } bool BatchedNMSDynamicPlugin::supportsFormatCombination( int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept { - ASSERT(0 <= pos && pos < 6); + ASSERT(nbInputs <= 2 && nbInputs >= 0); + ASSERT(nbOutputs <= 4 && nbOutputs >= 0); + ASSERT(pos < 6 && pos >= 0); const auto* in = inOut; const auto* out = inOut + nbInputs; const bool consistentFloatPrecision = in[0].type == in[pos].type; @@ -400,33 +497,56 @@ void BatchedNMSDynamicPlugin::destroy() noexcept IPluginV2Ext* BatchedNMSPlugin::clone() const noexcept { - auto* plugin = new BatchedNMSPlugin(param); - plugin->boxesSize = boxesSize; - plugin->scoresSize = scoresSize; - plugin->numPriors = numPriors; - plugin->setPluginNamespace(mNamespace.c_str()); - plugin->setClipParam(mClipBoxes); - plugin->mPrecision = mPrecision; - plugin->setScoreBits(mScoreBits); - return plugin; + try + { + auto* plugin = new BatchedNMSPlugin(param); + plugin->boxesSize = boxesSize; + plugin->scoresSize = scoresSize; + plugin->numPriors = numPriors; + plugin->setPluginNamespace(mNamespace.c_str()); + plugin->setClipParam(mClipBoxes); + plugin->mPrecision = mPrecision; + plugin->setScoreBits(mScoreBits); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } IPluginV2DynamicExt* BatchedNMSDynamicPlugin::clone() const noexcept { - auto* plugin = new BatchedNMSDynamicPlugin(param); - plugin->boxesSize = boxesSize; - plugin->scoresSize = scoresSize; - plugin->numPriors = numPriors; - plugin->setPluginNamespace(mNamespace.c_str()); - plugin->setClipParam(mClipBoxes); - plugin->mPrecision = mPrecision; - plugin->setScoreBits(mScoreBits); - return plugin; + try + { + auto* plugin = new BatchedNMSDynamicPlugin(param); + plugin->boxesSize = boxesSize; + plugin->scoresSize = scoresSize; + plugin->numPriors = numPriors; + plugin->setPluginNamespace(mNamespace.c_str()); + plugin->setClipParam(mClipBoxes); + plugin->mPrecision = mPrecision; + plugin->setScoreBits(mScoreBits); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } void BatchedNMSPlugin::setPluginNamespace(const char* pluginNamespace) noexcept { - mNamespace = pluginNamespace; + try + { + mNamespace = pluginNamespace; + } + catch (const std::exception& e) + { + caughtError(e); + } } const char* BatchedNMSPlugin::getPluginNamespace() const noexcept @@ -436,7 +556,14 @@ const char* BatchedNMSPlugin::getPluginNamespace() const noexcept void BatchedNMSDynamicPlugin::setPluginNamespace(const char* pluginNamespace) noexcept { - mNamespace = pluginNamespace; + try + { + mNamespace = pluginNamespace; + } + catch (const std::exception& e) + { + caughtError(e); + } } const char* BatchedNMSDynamicPlugin::getPluginNamespace() const noexcept @@ -484,8 +611,7 @@ void BatchedNMSDynamicPlugin::setScoreBits(int32_t scoreBits) noexcept mScoreBits = scoreBits; } -bool BatchedNMSPlugin::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const - noexcept +bool BatchedNMSPlugin::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept { return false; } @@ -495,8 +621,7 @@ bool BatchedNMSPlugin::canBroadcastInputAcrossBatch(int inputIndex) const noexce return false; } -BatchedNMSBasePluginCreator::BatchedNMSBasePluginCreator() noexcept - : params{} +BatchedNMSBasePluginCreator::BatchedNMSBasePluginCreator() { mPluginAttributes.clear(); mPluginAttributes.emplace_back(PluginField("shareLocation", nullptr, PluginFieldType::kINT32, 1)); @@ -513,19 +638,14 @@ BatchedNMSBasePluginCreator::BatchedNMSBasePluginCreator() noexcept mFC.fields = mPluginAttributes.data(); } -BatchedNMSPluginCreator::BatchedNMSPluginCreator() noexcept +const char* BatchedNMSPluginCreator::getPluginName() const noexcept { - mPluginName = NMS_PLUGIN_NAMES[0]; + return NMS_PLUGIN_NAMES[0]; } -BatchedNMSDynamicPluginCreator::BatchedNMSDynamicPluginCreator() noexcept +const char* BatchedNMSDynamicPluginCreator::getPluginName() const noexcept { - mPluginName = NMS_PLUGIN_NAMES[1]; -} - -const char* BatchedNMSBasePluginCreator::getPluginName() const noexcept -{ - return mPluginName.c_str(); + return NMS_PLUGIN_NAMES[1]; } const char* BatchedNMSBasePluginCreator::getPluginVersion() const noexcept @@ -540,147 +660,183 @@ const PluginFieldCollection* BatchedNMSBasePluginCreator::getFieldNames() noexce IPluginV2Ext* BatchedNMSPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { - const PluginField* fields = fc->fields; - mClipBoxes = true; - mScoreBits = 16; - for (int i = 0; i < fc->nbFields; ++i) + try { - const char* attrName = fields[i].name; - if (!strcmp(attrName, "shareLocation")) - { - params.shareLocation = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "backgroundLabelId")) - { - ASSERT(fields[i].type == PluginFieldType::kINT32); - params.backgroundLabelId = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "numClasses")) - { - ASSERT(fields[i].type == PluginFieldType::kINT32); - params.numClasses = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "topK")) - { - ASSERT(fields[i].type == PluginFieldType::kINT32); - params.topK = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "keepTopK")) - { - ASSERT(fields[i].type == PluginFieldType::kINT32); - params.keepTopK = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "scoreThreshold")) - { - ASSERT(fields[i].type == PluginFieldType::kFLOAT32); - params.scoreThreshold = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "iouThreshold")) - { - ASSERT(fields[i].type == PluginFieldType::kFLOAT32); - params.iouThreshold = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "isNormalized")) - { - params.isNormalized = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "clipBoxes")) - { - mClipBoxes = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "scoreBits")) - { - mScoreBits = *(static_cast(fields[i].data)); - } - } + NMSParameters params; + const PluginField* fields = fc->fields; + bool clipBoxes = true; + int32_t scoreBits = 16; - auto* plugin = new BatchedNMSPlugin(params); - plugin->setClipParam(mClipBoxes); - plugin->setScoreBits(mScoreBits); - plugin->setPluginNamespace(mNamespace.c_str()); - return plugin; + for (int i = 0; i < fc->nbFields; ++i) + { + const char* attrName = fields[i].name; + if (!strcmp(attrName, "shareLocation")) + { + params.shareLocation = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "backgroundLabelId")) + { + ASSERT(fields[i].type == PluginFieldType::kINT32); + params.backgroundLabelId = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "numClasses")) + { + ASSERT(fields[i].type == PluginFieldType::kINT32); + params.numClasses = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "topK")) + { + ASSERT(fields[i].type == PluginFieldType::kINT32); + params.topK = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "keepTopK")) + { + ASSERT(fields[i].type == PluginFieldType::kINT32); + params.keepTopK = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "scoreThreshold")) + { + ASSERT(fields[i].type == PluginFieldType::kFLOAT32); + params.scoreThreshold = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "iouThreshold")) + { + ASSERT(fields[i].type == PluginFieldType::kFLOAT32); + params.iouThreshold = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "isNormalized")) + { + params.isNormalized = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "clipBoxes")) + { + clipBoxes = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "scoreBits")) + { + scoreBits = *(static_cast(fields[i].data)); + } + } + + auto* plugin = new BatchedNMSPlugin(params); + plugin->setClipParam(clipBoxes); + plugin->setScoreBits(scoreBits); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } IPluginV2DynamicExt* BatchedNMSDynamicPluginCreator::createPlugin( const char* name, const PluginFieldCollection* fc) noexcept { - const PluginField* fields = fc->fields; - mClipBoxes = true; - mScoreBits = 16; - for (int i = 0; i < fc->nbFields; ++i) + try { - const char* attrName = fields[i].name; - if (!strcmp(attrName, "shareLocation")) - { - params.shareLocation = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "backgroundLabelId")) - { - ASSERT(fields[i].type == PluginFieldType::kINT32); - params.backgroundLabelId = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "numClasses")) - { - ASSERT(fields[i].type == PluginFieldType::kINT32); - params.numClasses = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "topK")) - { - ASSERT(fields[i].type == PluginFieldType::kINT32); - params.topK = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "keepTopK")) - { - ASSERT(fields[i].type == PluginFieldType::kINT32); - params.keepTopK = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "scoreThreshold")) - { - ASSERT(fields[i].type == PluginFieldType::kFLOAT32); - params.scoreThreshold = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "iouThreshold")) - { - ASSERT(fields[i].type == PluginFieldType::kFLOAT32); - params.iouThreshold = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "isNormalized")) - { - params.isNormalized = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "clipBoxes")) - { - mClipBoxes = *(static_cast(fields[i].data)); - } - else if (!strcmp(attrName, "scoreBits")) - { - mScoreBits = *(static_cast(fields[i].data)); - } - } + NMSParameters params; + const PluginField* fields = fc->fields; + bool clipBoxes = true; + int32_t scoreBits = 16; - auto* plugin = new BatchedNMSDynamicPlugin(params); - plugin->setClipParam(mClipBoxes); - plugin->setScoreBits(mScoreBits); - plugin->setPluginNamespace(mNamespace.c_str()); - return plugin; + for (int i = 0; i < fc->nbFields; ++i) + { + const char* attrName = fields[i].name; + if (!strcmp(attrName, "shareLocation")) + { + params.shareLocation = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "backgroundLabelId")) + { + ASSERT(fields[i].type == PluginFieldType::kINT32); + params.backgroundLabelId = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "numClasses")) + { + ASSERT(fields[i].type == PluginFieldType::kINT32); + params.numClasses = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "topK")) + { + ASSERT(fields[i].type == PluginFieldType::kINT32); + params.topK = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "keepTopK")) + { + ASSERT(fields[i].type == PluginFieldType::kINT32); + params.keepTopK = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "scoreThreshold")) + { + ASSERT(fields[i].type == PluginFieldType::kFLOAT32); + params.scoreThreshold = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "iouThreshold")) + { + ASSERT(fields[i].type == PluginFieldType::kFLOAT32); + params.iouThreshold = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "isNormalized")) + { + params.isNormalized = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "clipBoxes")) + { + clipBoxes = *(static_cast(fields[i].data)); + } + else if (!strcmp(attrName, "scoreBits")) + { + scoreBits = *(static_cast(fields[i].data)); + } + } + + auto* plugin = new BatchedNMSDynamicPlugin(params); + plugin->setClipParam(clipBoxes); + plugin->setScoreBits(scoreBits); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } IPluginV2Ext* BatchedNMSPluginCreator::deserializePlugin( const char* name, const void* serialData, size_t serialLength) noexcept { - // This object will be deleted when the network is destroyed, which will - // call NMS::destroy() - auto* plugin = new BatchedNMSPlugin(serialData, serialLength); - plugin->setPluginNamespace(mNamespace.c_str()); - return plugin; + try + { + // This object will be deleted when the network is destroyed, which will + // call NMS::destroy() + auto* plugin = new BatchedNMSPlugin(serialData, serialLength); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } IPluginV2DynamicExt* BatchedNMSDynamicPluginCreator::deserializePlugin( const char* name, const void* serialData, size_t serialLength) noexcept { - // This object will be deleted when the network is destroyed, which will - // call NMS::destroy() - auto* plugin = new BatchedNMSDynamicPlugin(serialData, serialLength); - plugin->setPluginNamespace(mNamespace.c_str()); - return plugin; + try + { + // This object will be deleted when the network is destroyed, which will + // call NMS::destroy() + auto* plugin = new BatchedNMSDynamicPlugin(serialData, serialLength); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } diff --git a/plugin/batchedNMSPlugin/batchedNMSPlugin.h b/plugin/batchedNMSPlugin/batchedNMSPlugin.h index 4c4d82ea..2c441340 100644 --- a/plugin/batchedNMSPlugin/batchedNMSPlugin.h +++ b/plugin/batchedNMSPlugin/batchedNMSPlugin.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_BATCHED_NMS_PLUGIN_H #define TRT_BATCHED_NMS_PLUGIN_H #include "gatherNMSOutputs.h" @@ -32,9 +31,9 @@ namespace plugin class BatchedNMSPlugin : public IPluginV2Ext { public: - BatchedNMSPlugin(NMSParameters param) noexcept; - BatchedNMSPlugin(const void* data, size_t length) noexcept; - ~BatchedNMSPlugin() noexcept override = default; + BatchedNMSPlugin(NMSParameters param); + BatchedNMSPlugin(const void* data, size_t length); + ~BatchedNMSPlugin() override = default; // IPluginV2 methods const char* getPluginType() const noexcept override; @@ -43,7 +42,7 @@ public: Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept override; bool supportsFormat(DataType type, PluginFormat format) const noexcept override; size_t getWorkspaceSize(int maxBatchSize) const noexcept override; - int enqueue(int batchSize, const void* const* inputs, void** outputs, void* workspace, + int32_t enqueue(int32_t batchSize, void const* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; int initialize() noexcept override; void terminate() noexcept override; @@ -56,7 +55,7 @@ public: void setScoreBits(int32_t scoreBits) noexcept; // IPluginV2Ext methods - nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputType, int nbInputs) const + nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept override; bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept override; @@ -75,14 +74,15 @@ private: bool mClipBoxes{}; DataType mPrecision; int32_t mScoreBits; + pluginStatus_t mPluginStatus{}; }; class BatchedNMSDynamicPlugin : public IPluginV2DynamicExt { public: - BatchedNMSDynamicPlugin(NMSParameters param) noexcept; - BatchedNMSDynamicPlugin(const void* data, size_t length) noexcept; - ~BatchedNMSDynamicPlugin() noexcept override = default; + BatchedNMSDynamicPlugin(NMSParameters param); + BatchedNMSDynamicPlugin(const void* data, size_t length); + ~BatchedNMSDynamicPlugin() override = default; // IPluginV2 methods const char* getPluginType() const noexcept override; @@ -99,19 +99,17 @@ public: void setScoreBits(int32_t scoreBits) noexcept; // IPluginV2Ext methods - nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputType, int nbInputs) const - noexcept override; + nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputType, int nbInputs) const noexcept override; // IPluginV2DynamicExt methods IPluginV2DynamicExt* clone() const noexcept override; DimsExprs getOutputDimensions( int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) noexcept override; - bool supportsFormatCombination( - int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept override; - void configurePlugin(const DynamicPluginTensorDesc* in, int nbInputs, const DynamicPluginTensorDesc* out, - int nbOutputs) noexcept override; - size_t getWorkspaceSize(const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, - int nbOutputs) const noexcept override; + bool supportsFormatCombination(int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept override; + void configurePlugin( + const DynamicPluginTensorDesc* in, int nbInputs, const DynamicPluginTensorDesc* out, int nbOutputs) noexcept override; + size_t getWorkspaceSize( + const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const noexcept override; int enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; @@ -124,33 +122,27 @@ private: bool mClipBoxes{}; DataType mPrecision; int32_t mScoreBits; + pluginStatus_t mPluginStatus{}; }; class BatchedNMSBasePluginCreator : public BaseCreator { public: - BatchedNMSBasePluginCreator() noexcept; - ~BatchedNMSBasePluginCreator() noexcept override = default; + BatchedNMSBasePluginCreator(); + ~BatchedNMSBasePluginCreator() override = default; - const char* getPluginName() const noexcept override; const char* getPluginVersion() const noexcept override; const PluginFieldCollection* getFieldNames() noexcept override; protected: static PluginFieldCollection mFC; - NMSParameters params; static std::vector mPluginAttributes; - bool mClipBoxes; - int32_t mScoreBits; - std::string mPluginName; }; class BatchedNMSPluginCreator : public BatchedNMSBasePluginCreator { public: - BatchedNMSPluginCreator() noexcept; - ~BatchedNMSPluginCreator() noexcept override = default; - + const char* getPluginName() const noexcept override; IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; }; @@ -158,9 +150,7 @@ public: class BatchedNMSDynamicPluginCreator : public BatchedNMSBasePluginCreator { public: - BatchedNMSDynamicPluginCreator() noexcept; - ~BatchedNMSDynamicPluginCreator() noexcept override = default; - + const char* getPluginName() const noexcept override; IPluginV2DynamicExt* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; IPluginV2DynamicExt* deserializePlugin( const char* name, const void* serialData, size_t serialLength) noexcept override; diff --git a/plugin/batchedNMSPlugin/gatherNMSOutputs.h b/plugin/batchedNMSPlugin/gatherNMSOutputs.h index 5001f253..b6a8feaf 100644 --- a/plugin/batchedNMSPlugin/gatherNMSOutputs.h +++ b/plugin/batchedNMSPlugin/gatherNMSOutputs.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_BATCHED_NMS_HELPER_H #define TRT_BATCHED_NMS_HELPER_H #include "plugin.h" diff --git a/plugin/bertQKVToContextPlugin/fused_multihead_attention.h b/plugin/bertQKVToContextPlugin/fused_multihead_attention.h index b797b862..fd29fd7c 100644 --- a/plugin/bertQKVToContextPlugin/fused_multihead_attention.h +++ b/plugin/bertQKVToContextPlugin/fused_multihead_attention.h @@ -100,31 +100,31 @@ extern unsigned char fused_multihead_attention_fp16_128_64_kernel_sm80_cu_o[]; extern unsigned char fused_multihead_attention_fp16_384_64_kernel_sm80_cu_o[]; extern unsigned char fused_multihead_attention_fp16_384_64_kernel_sm86_cu_o[]; -extern unsigned int fused_multihead_attention_fp16_64_64_kernel_sm75_cu_o_len; -extern unsigned int fused_multihead_attention_fp16_96_64_kernel_sm75_cu_o_len; -extern unsigned int fused_multihead_attention_fp16_64_64_kernel_sm80_cu_o_len; -extern unsigned int fused_multihead_attention_fp16_96_64_kernel_sm80_cu_o_len; -extern unsigned int fused_multihead_attention_fp16_128_64_kernel_sm75_cu_o_len; -extern unsigned int fused_multihead_attention_fp16_384_64_kernel_sm75_cu_o_len; -extern unsigned int fused_multihead_attention_int8_128_64_kernel_sm75_cu_o_len; -extern unsigned int fused_multihead_attention_int8_384_64_kernel_sm75_cu_o_len; -extern unsigned int fused_multihead_attention_int8_384_64_kernel_sm80_cu_o_len; -extern unsigned int fused_multihead_attention_int8_128_64_kernel_sm80_cu_o_len; -extern unsigned int fused_multihead_attention_fp16_128_64_kernel_sm80_cu_o_len; -extern unsigned int fused_multihead_attention_fp16_384_64_kernel_sm80_cu_o_len; -extern unsigned int fused_multihead_attention_fp16_384_64_kernel_sm86_cu_o_len; +extern uint32_t fused_multihead_attention_fp16_64_64_kernel_sm75_cu_o_len; +extern uint32_t fused_multihead_attention_fp16_96_64_kernel_sm75_cu_o_len; +extern uint32_t fused_multihead_attention_fp16_64_64_kernel_sm80_cu_o_len; +extern uint32_t fused_multihead_attention_fp16_96_64_kernel_sm80_cu_o_len; +extern uint32_t fused_multihead_attention_fp16_128_64_kernel_sm75_cu_o_len; +extern uint32_t fused_multihead_attention_fp16_384_64_kernel_sm75_cu_o_len; +extern uint32_t fused_multihead_attention_int8_128_64_kernel_sm75_cu_o_len; +extern uint32_t fused_multihead_attention_int8_384_64_kernel_sm75_cu_o_len; +extern uint32_t fused_multihead_attention_int8_384_64_kernel_sm80_cu_o_len; +extern uint32_t fused_multihead_attention_int8_128_64_kernel_sm80_cu_o_len; +extern uint32_t fused_multihead_attention_fp16_128_64_kernel_sm80_cu_o_len; +extern uint32_t fused_multihead_attention_fp16_384_64_kernel_sm80_cu_o_len; +extern uint32_t fused_multihead_attention_fp16_384_64_kernel_sm86_cu_o_len; static const struct FusedMultiHeadAttentionKernelMetaInfoV1 { Data_type mDataType; - unsigned int mS; - unsigned int mD; - unsigned int mSM; + uint32_t mS; + uint32_t mD; + uint32_t mSM; const unsigned char* mCubin; - unsigned int mCubinSize; + uint32_t mCubinSize; const char* mFuncName; - unsigned int mSharedMemBytes; - unsigned int mThreadsPerCTA; + uint32_t mSharedMemBytes; + uint32_t mThreadsPerCTA; } sMhaKernelMetaInfos[] = { // Turing {DATA_TYPE_FP16, 64, 64, kSM_75, fused_multihead_attention_fp16_64_64_kernel_sm75_cu_o, @@ -196,7 +196,7 @@ class TFusedMultiHeadAttentionXMMAKernel public: using KernelMeta = TKernelMeta; using KernelParam = TKernelParam; - inline uint64_t hashID(unsigned int s, unsigned int d) const + inline uint64_t hashID(uint32_t s, uint32_t d) const { return (uint64_t) s << 32 | d; } @@ -205,8 +205,7 @@ public: return hashID(kernelMeta.mS, kernelMeta.mD); } - TFusedMultiHeadAttentionXMMAKernel( - const TKernelMeta* pMetaStart, unsigned int nMetaCount, Data_type type, unsigned int sm) + TFusedMultiHeadAttentionXMMAKernel(const TKernelMeta* pMetaStart, uint32_t nMetaCount, Data_type type, uint32_t sm) : mDataType(type) , mKernelMeta(pMetaStart) , mKernelMetaCount(nMetaCount) @@ -221,7 +220,7 @@ public: return; } - for (unsigned int i = 0; i < mKernelMetaCount; ++i) + for (uint32_t i = 0; i < mKernelMetaCount; ++i) { const auto& kernelMeta = mKernelMeta[i]; if (kernelMeta.mSM == mSM && kernelMeta.mDataType == mDataType) @@ -281,12 +280,12 @@ protected: Data_type mDataType; const TKernelMeta* mKernelMeta; - unsigned int mKernelMetaCount; - unsigned int mSM; + uint32_t mKernelMetaCount; + uint32_t mSM; std::unordered_map mModules; struct FusedMultiHeadAttentionKernelInfo { - unsigned int mMetaInfoIndex; + uint32_t mMetaInfoIndex; CUfunction mDeviceFunction; }; std::unordered_map mFunctions; @@ -297,8 +296,8 @@ template class TFusedMHAKernelFactory { public: - const TFusedMHAKernelList* getXMMAKernels(const typename TFusedMHAKernelList::KernelMeta* pKernelList, - unsigned int nbKernels, Data_type type, unsigned int sm) + const TFusedMHAKernelList* getXMMAKernels( + const typename TFusedMHAKernelList::KernelMeta* pKernelList, uint32_t nbKernels, Data_type type, uint32_t sm) { static std::mutex s_mutex; std::lock_guard lg(s_mutex); @@ -324,9 +323,16 @@ public: private: TFusedMHAKernelFactory() = default; - inline uint64_t hashID(Data_type type, unsigned int sm) const + inline uint64_t hashID(Data_type type, uint32_t sm) const { - return (uint64_t) type << 32 | sm; + // use deviceID in hasID for multi GPU support before driver support context-less loading of cubin + int32_t deviceID{0}; + cudaGetDevice(&deviceID); + + ASSERT((deviceID & 0xFFFF) == deviceID); + ASSERT((type & 0xFFFF) == type); + ASSERT((sm & 0xFFFFFFFF) == sm); + return (uint64_t) type << 48 | (uint64_t) deviceID << 32 | sm; } std::unordered_map> mKernels; @@ -336,7 +342,7 @@ using FusedMultiHeadAttentionXMMAKernel = TFusedMultiHeadAttentionXMMAKernel; using FusedMHAKernelFactory = TFusedMHAKernelFactory; -inline const FusedMultiHeadAttentionXMMAKernel* getXMMAKernels(Data_type type, unsigned int sm) +inline const FusedMultiHeadAttentionXMMAKernel* getXMMAKernels(Data_type type, uint32_t sm) { return FusedMHAKernelFactory::Get().getXMMAKernels( sMhaKernelMetaInfos, sizeof(sMhaKernelMetaInfos) / sizeof(sMhaKernelMetaInfos[0]), type, sm); diff --git a/plugin/bertQKVToContextPlugin/fused_multihead_attention_common.h b/plugin/bertQKVToContextPlugin/fused_multihead_attention_common.h index 83214dc3..0e41db2e 100644 --- a/plugin/bertQKVToContextPlugin/fused_multihead_attention_common.h +++ b/plugin/bertQKVToContextPlugin/fused_multihead_attention_common.h @@ -16,7 +16,7 @@ #pragma once -#include +#include namespace bert { enum Data_type diff --git a/plugin/bertQKVToContextPlugin/fused_multihead_attention_v2.h b/plugin/bertQKVToContextPlugin/fused_multihead_attention_v2.h index 46813dac..55a33abf 100644 --- a/plugin/bertQKVToContextPlugin/fused_multihead_attention_v2.h +++ b/plugin/bertQKVToContextPlugin/fused_multihead_attention_v2.h @@ -17,8 +17,8 @@ #pragma once #include "fused_multihead_attention.h" #include "fused_multihead_attention_common.h" -#include -#include +#include +#include namespace bert { @@ -141,50 +141,50 @@ extern unsigned char fused_multihead_attention_v2_int8_384_64_kernel_sm75_cubin[ extern unsigned char fused_multihead_attention_v2_int8_384_64_kernel_sm80_cubin[]; extern unsigned char fused_multihead_attention_v2_int8_384_64_kernel_sm86_cubin[]; -extern unsigned int fused_multihead_attention_v2_fp16_128_64_kernel_sm75_cubin_len; -extern unsigned int fused_multihead_attention_v2_fp16_128_64_kernel_sm80_cubin_len; -extern unsigned int fused_multihead_attention_v2_fp16_128_64_kernel_sm86_cubin_len; -extern unsigned int fused_multihead_attention_v2_fp16_256_64_kernel_sm75_cubin_len; -extern unsigned int fused_multihead_attention_v2_fp16_256_64_kernel_sm80_cubin_len; -extern unsigned int fused_multihead_attention_v2_fp16_256_64_kernel_sm86_cubin_len; -extern unsigned int fused_multihead_attention_v2_fp16_384_64_kernel_sm75_cubin_len; -extern unsigned int fused_multihead_attention_v2_fp16_384_64_kernel_sm80_cubin_len; -extern unsigned int fused_multihead_attention_v2_fp16_384_64_kernel_sm86_cubin_len; -extern unsigned int fused_multihead_attention_v2_fp16_64_64_kernel_sm75_cubin_len; -extern unsigned int fused_multihead_attention_v2_fp16_64_64_kernel_sm80_cubin_len; -extern unsigned int fused_multihead_attention_v2_fp16_64_64_kernel_sm86_cubin_len; -extern unsigned int fused_multihead_attention_v2_fp16_96_64_kernel_sm75_cubin_len; -extern unsigned int fused_multihead_attention_v2_fp16_96_64_kernel_sm80_cubin_len; -extern unsigned int fused_multihead_attention_v2_fp16_96_64_kernel_sm86_cubin_len; -extern unsigned int fused_multihead_attention_v2_int8_128_64_kernel_cubin_len; -extern unsigned int fused_multihead_attention_v2_int8_128_64_kernel_sm75_cubin_len; -extern unsigned int fused_multihead_attention_v2_int8_128_64_kernel_sm80_cubin_len; -extern unsigned int fused_multihead_attention_v2_int8_128_64_kernel_sm86_cubin_len; -extern unsigned int fused_multihead_attention_v2_int8_192_64_kernel_cubin_len; -extern unsigned int fused_multihead_attention_v2_int8_192_64_kernel_sm75_cubin_len; -extern unsigned int fused_multihead_attention_v2_int8_192_64_kernel_sm80_cubin_len; -extern unsigned int fused_multihead_attention_v2_int8_192_64_kernel_sm86_cubin_len; -extern unsigned int fused_multihead_attention_v2_int8_256_64_kernel_cubin_len; -extern unsigned int fused_multihead_attention_v2_int8_256_64_kernel_sm75_cubin_len; -extern unsigned int fused_multihead_attention_v2_int8_256_64_kernel_sm80_cubin_len; -extern unsigned int fused_multihead_attention_v2_int8_256_64_kernel_sm86_cubin_len; -extern unsigned int fused_multihead_attention_v2_int8_384_64_kernel_cubin_len; -extern unsigned int fused_multihead_attention_v2_int8_384_64_kernel_sm75_cubin_len; -extern unsigned int fused_multihead_attention_v2_int8_384_64_kernel_sm80_cubin_len; -extern unsigned int fused_multihead_attention_v2_int8_384_64_kernel_sm86_cubin_len; +extern uint32_t fused_multihead_attention_v2_fp16_128_64_kernel_sm75_cubin_len; +extern uint32_t fused_multihead_attention_v2_fp16_128_64_kernel_sm80_cubin_len; +extern uint32_t fused_multihead_attention_v2_fp16_128_64_kernel_sm86_cubin_len; +extern uint32_t fused_multihead_attention_v2_fp16_256_64_kernel_sm75_cubin_len; +extern uint32_t fused_multihead_attention_v2_fp16_256_64_kernel_sm80_cubin_len; +extern uint32_t fused_multihead_attention_v2_fp16_256_64_kernel_sm86_cubin_len; +extern uint32_t fused_multihead_attention_v2_fp16_384_64_kernel_sm75_cubin_len; +extern uint32_t fused_multihead_attention_v2_fp16_384_64_kernel_sm80_cubin_len; +extern uint32_t fused_multihead_attention_v2_fp16_384_64_kernel_sm86_cubin_len; +extern uint32_t fused_multihead_attention_v2_fp16_64_64_kernel_sm75_cubin_len; +extern uint32_t fused_multihead_attention_v2_fp16_64_64_kernel_sm80_cubin_len; +extern uint32_t fused_multihead_attention_v2_fp16_64_64_kernel_sm86_cubin_len; +extern uint32_t fused_multihead_attention_v2_fp16_96_64_kernel_sm75_cubin_len; +extern uint32_t fused_multihead_attention_v2_fp16_96_64_kernel_sm80_cubin_len; +extern uint32_t fused_multihead_attention_v2_fp16_96_64_kernel_sm86_cubin_len; +extern uint32_t fused_multihead_attention_v2_int8_128_64_kernel_cubin_len; +extern uint32_t fused_multihead_attention_v2_int8_128_64_kernel_sm75_cubin_len; +extern uint32_t fused_multihead_attention_v2_int8_128_64_kernel_sm80_cubin_len; +extern uint32_t fused_multihead_attention_v2_int8_128_64_kernel_sm86_cubin_len; +extern uint32_t fused_multihead_attention_v2_int8_192_64_kernel_cubin_len; +extern uint32_t fused_multihead_attention_v2_int8_192_64_kernel_sm75_cubin_len; +extern uint32_t fused_multihead_attention_v2_int8_192_64_kernel_sm80_cubin_len; +extern uint32_t fused_multihead_attention_v2_int8_192_64_kernel_sm86_cubin_len; +extern uint32_t fused_multihead_attention_v2_int8_256_64_kernel_cubin_len; +extern uint32_t fused_multihead_attention_v2_int8_256_64_kernel_sm75_cubin_len; +extern uint32_t fused_multihead_attention_v2_int8_256_64_kernel_sm80_cubin_len; +extern uint32_t fused_multihead_attention_v2_int8_256_64_kernel_sm86_cubin_len; +extern uint32_t fused_multihead_attention_v2_int8_384_64_kernel_cubin_len; +extern uint32_t fused_multihead_attention_v2_int8_384_64_kernel_sm75_cubin_len; +extern uint32_t fused_multihead_attention_v2_int8_384_64_kernel_sm80_cubin_len; +extern uint32_t fused_multihead_attention_v2_int8_384_64_kernel_sm86_cubin_len; static const struct FusedMultiHeadAttentionKernelMetaInfoV2 { Data_type mDataType; - unsigned int mS; - unsigned int mD; - unsigned int mSM; + uint32_t mS; + uint32_t mD; + uint32_t mSM; const unsigned char* mCubin; - unsigned int mCubinSize; + uint32_t mCubinSize; const char* mFuncName; - unsigned int mSharedMemBytes; - unsigned int mThreadsPerCTA; - unsigned int mUnrollStep; + uint32_t mSharedMemBytes; + uint32_t mThreadsPerCTA; + uint32_t mUnrollStep; bool mInterleaved; } sMhaKernelMetaInfosV2[] = { // Xavier @@ -391,6 +391,7 @@ static const struct FusedMultiHeadAttentionKernelMetaInfoV2 fused_multihead_attention_v2_fp16_384_64_kernel_sm86_cubin_len, "fused_multihead_attention_v2_fp16_384_64_kernel_sm80", 65536, 256, 0, false}, + {DATA_TYPE_INT8, 128, 64, kSM_86, fused_multihead_attention_v2_int8_128_64_kernel_sm86_cubin, fused_multihead_attention_v2_int8_128_64_kernel_sm86_cubin_len, "fused_multihead_attention_v2_int8_128_64_kernel_sm80_interleaved_noloop", 20480, 128, 16, true}, @@ -405,40 +406,40 @@ static const struct FusedMultiHeadAttentionKernelMetaInfoV2 "fused_multihead_attention_v2_int8_128_64_kernel_sm80", 32768, 128, 0, false}, {DATA_TYPE_INT8, 192, 64, kSM_86, fused_multihead_attention_v2_int8_192_64_kernel_sm86_cubin, fused_multihead_attention_v2_int8_192_64_kernel_sm86_cubin_len, - "fused_multihead_attention_v2_int8_192_64_kernel_sm80_interleaved_noloop", 28672, 128, 32, true}, + "fused_multihead_attention_v2_int8_192_64_kernel_sm86_interleaved_noloop", 28672, 128, 32, true}, {DATA_TYPE_INT8, 192, 64, kSM_86, fused_multihead_attention_v2_int8_192_64_kernel_sm86_cubin, fused_multihead_attention_v2_int8_192_64_kernel_sm86_cubin_len, - "fused_multihead_attention_v2_int8_192_64_kernel_sm80_noloop", 28672, 128, 32, false}, + "fused_multihead_attention_v2_int8_192_64_kernel_sm86_noloop", 28672, 128, 32, false}, {DATA_TYPE_INT8, 192, 64, kSM_86, fused_multihead_attention_v2_int8_192_64_kernel_sm86_cubin, fused_multihead_attention_v2_int8_192_64_kernel_sm86_cubin_len, - "fused_multihead_attention_v2_int8_192_64_kernel_sm80_interleaved", 32768, 128, 0, true}, + "fused_multihead_attention_v2_int8_192_64_kernel_sm86_interleaved", 32768, 128, 0, true}, {DATA_TYPE_INT8, 192, 64, kSM_86, fused_multihead_attention_v2_int8_192_64_kernel_sm86_cubin, fused_multihead_attention_v2_int8_192_64_kernel_sm86_cubin_len, - "fused_multihead_attention_v2_int8_192_64_kernel_sm80", 32768, 128, 0, false}, + "fused_multihead_attention_v2_int8_192_64_kernel_sm86", 32768, 128, 0, false}, {DATA_TYPE_INT8, 256, 64, kSM_86, fused_multihead_attention_v2_int8_256_64_kernel_sm86_cubin, fused_multihead_attention_v2_int8_256_64_kernel_sm86_cubin_len, - "fused_multihead_attention_v2_int8_256_64_kernel_sm80_interleaved_noloop", 36864, 128, 32, true}, + "fused_multihead_attention_v2_int8_256_64_kernel_sm86_interleaved_noloop", 36864, 128, 32, true}, {DATA_TYPE_INT8, 256, 64, kSM_86, fused_multihead_attention_v2_int8_256_64_kernel_sm86_cubin, fused_multihead_attention_v2_int8_256_64_kernel_sm86_cubin_len, - "fused_multihead_attention_v2_int8_256_64_kernel_sm80_noloop", 36864, 128, 32, false}, + "fused_multihead_attention_v2_int8_256_64_kernel_sm86_noloop", 36864, 128, 32, false}, {DATA_TYPE_INT8, 256, 64, kSM_86, fused_multihead_attention_v2_int8_256_64_kernel_sm86_cubin, fused_multihead_attention_v2_int8_256_64_kernel_sm86_cubin_len, - "fused_multihead_attention_v2_int8_256_64_kernel_sm80_interleaved", 36864, 128, 0, true}, + "fused_multihead_attention_v2_int8_256_64_kernel_sm86_interleaved", 36864, 128, 0, true}, {DATA_TYPE_INT8, 256, 64, kSM_86, fused_multihead_attention_v2_int8_256_64_kernel_sm86_cubin, fused_multihead_attention_v2_int8_256_64_kernel_sm86_cubin_len, - "fused_multihead_attention_v2_int8_256_64_kernel_sm80", 36864, 128, 0, false}, + "fused_multihead_attention_v2_int8_256_64_kernel_sm86", 36864, 128, 0, false}, {DATA_TYPE_INT8, 384, 64, kSM_86, fused_multihead_attention_v2_int8_384_64_kernel_sm86_cubin, fused_multihead_attention_v2_int8_384_64_kernel_sm86_cubin_len, - "fused_multihead_attention_v2_int8_384_64_kernel_sm80_interleaved_noloop", 53248, 128, 32, true}, + "fused_multihead_attention_v2_int8_384_64_kernel_sm86_interleaved_noloop", 28672, 128, 32, true}, {DATA_TYPE_INT8, 384, 64, kSM_86, fused_multihead_attention_v2_int8_384_64_kernel_sm86_cubin, fused_multihead_attention_v2_int8_384_64_kernel_sm86_cubin_len, - "fused_multihead_attention_v2_int8_384_64_kernel_sm80_noloop", 53248, 128, 32, false}, + "fused_multihead_attention_v2_int8_384_64_kernel_sm86_noloop", 28672, 128, 32, false}, {DATA_TYPE_INT8, 384, 64, kSM_86, fused_multihead_attention_v2_int8_384_64_kernel_sm86_cubin, fused_multihead_attention_v2_int8_384_64_kernel_sm86_cubin_len, - "fused_multihead_attention_v2_int8_384_64_kernel_sm80_interleaved", 51200, 128, 0, true}, + "fused_multihead_attention_v2_int8_384_64_kernel_sm86_interleaved", 28672, 128, 0, true}, {DATA_TYPE_INT8, 384, 64, kSM_86, fused_multihead_attention_v2_int8_384_64_kernel_sm86_cubin, fused_multihead_attention_v2_int8_384_64_kernel_sm86_cubin_len, - "fused_multihead_attention_v2_int8_384_64_kernel_sm80", 53248, 128, 0, false}, + "fused_multihead_attention_v2_int8_384_64_kernel_sm86", 28672, 128, 0, false}, #endif }; @@ -447,14 +448,14 @@ class FusedMultiHeadAttentionXMMAKernelV2 Fused_multihead_attention_params_v2> { public: - FusedMultiHeadAttentionXMMAKernelV2(const FusedMultiHeadAttentionKernelMetaInfoV2* pMetaStart, - unsigned int nMetaCount, Data_type type, unsigned int sm) + FusedMultiHeadAttentionXMMAKernelV2( + const FusedMultiHeadAttentionKernelMetaInfoV2* pMetaStart, uint32_t nMetaCount, Data_type type, uint32_t sm) : TFusedMultiHeadAttentionXMMAKernel(pMetaStart, nMetaCount, type, sm) { } - inline uint64_t hashID(unsigned int s, bool interleaved, bool unroll) const + inline uint64_t hashID(uint32_t s, bool interleaved, bool unroll) const { return (uint64_t) s << 32 | (interleaved ? 2ull : 0ull) | (unroll ? 1ull : 0ull); } @@ -478,7 +479,7 @@ public: { const struct { - unsigned int mSM; + uint32_t mSM; Data_type mDataType; int mS; int mMaxBatch; @@ -506,7 +507,7 @@ public: {kSM_86, bert::DATA_TYPE_INT8, 384, 8}, #endif }; - for (unsigned int i = 0u; i < sizeof(unrollList) / sizeof(unrollList[0]); ++i) + for (uint32_t i = 0u; i < sizeof(unrollList) / sizeof(unrollList[0]); ++i) { if (mSM == unrollList[i].mSM && mDataType == unrollList[i].mDataType && params.s == unrollList[i].mS && params.b <= unrollList[i].mMaxBatch) @@ -543,7 +544,7 @@ public: using FusedMHAKernelFactoryV2 = TFusedMHAKernelFactory; -inline const FusedMultiHeadAttentionXMMAKernelV2* getXMMAKernelsV2(Data_type type, unsigned int sm) +inline const FusedMultiHeadAttentionXMMAKernelV2* getXMMAKernelsV2(Data_type type, uint32_t sm) { return FusedMHAKernelFactoryV2::Get().getXMMAKernels( sMhaKernelMetaInfosV2, sizeof(sMhaKernelMetaInfosV2) / sizeof(sMhaKernelMetaInfosV2[0]), type, sm); diff --git a/plugin/bertQKVToContextPlugin/qkvToContext.cu b/plugin/bertQKVToContextPlugin/qkvToContext.cu index 14710371..24c90497 100644 --- a/plugin/bertQKVToContextPlugin/qkvToContext.cu +++ b/plugin/bertQKVToContextPlugin/qkvToContext.cu @@ -333,7 +333,8 @@ int computeMaskedScaledSoftmax(cudaStream_t stream, const int ld, const int B, c return 0; } -std::pair tuneBatchedGemm(const int B, const int S, const int numHeads, const int headSize) +std::pair tuneBatchedGemm( + const int B, const int S, const int numHeads, const int headSize, const int smVersion) { const int nruns = 500; cublasHandle_t cublas; @@ -378,6 +379,8 @@ std::pair tuneBatchedGemm(const int B, const int S, const int numHeads int best2 = startAlgo; float ms1 = 1000000; float ms2 = 1000000; + + ASSERT(smVersion >= kSM_53); for (int a = startAlgo; a <= endAlgo; a++) { cublasGemmAlgo_t algo = static_cast(a); @@ -439,12 +442,12 @@ template int computeMaskedScaledSoftmax(cudaStream_t stream, const int ld template int computeMaskedScaledSoftmax(cudaStream_t stream, const int ld, const int B, const int N, const float rsqrtHeadSize, const int* maskIdx, const half* input, half* output); -size_t MHARunner::getSerializationSize() const +size_t MHARunner::getSerializationSize() const noexcept { return sizeof(mS) + sizeof(mB); } -void MHARunner::serialize(void* buffer) const +void MHARunner::serialize(void* buffer) const noexcept { serialize_value(&buffer, mS); serialize_value(&buffer, mB); @@ -457,11 +460,12 @@ void MHARunner::deserialize(const void* data, size_t length) setup(mS, mB); } -UnfusedMHARunner::UnfusedMHARunner(const nvinfer1::DataType type, const int numHeads, const int headSize) +UnfusedMHARunner::UnfusedMHARunner(const nvinfer1::DataType type, const int numHeads, const int headSize, const int sm) : MHARunner(type, numHeads, headSize) , mIsBestAlgoFound(false) , mAlgoBatchedEx1(CUBLAS_GEMM_DEFAULT_TENSOR_OP) , mAlgoBatchedEx2(CUBLAS_GEMM_DEFAULT_TENSOR_OP) + , mSm(sm) { CUBLASASSERT(cublasCreate(&mCublas)); } @@ -471,12 +475,12 @@ UnfusedMHARunner::~UnfusedMHARunner() CUBLASASSERT(cublasDestroy(mCublas)); } -size_t UnfusedMHARunner::getSerializationSize() const +size_t UnfusedMHARunner::getSerializationSize() const noexcept { return sizeof(mAlgoBatchedEx1) + sizeof(mAlgoBatchedEx2) + MHARunner::getSerializationSize(); } -void UnfusedMHARunner::serialize(void* buffer) const +void UnfusedMHARunner::serialize(void* buffer) const noexcept { serialize_value(&buffer, mAlgoBatchedEx1); serialize_value(&buffer, mAlgoBatchedEx2); @@ -496,7 +500,7 @@ void UnfusedMHARunner::setup(const int S, const int B) MHARunner::setup(S, B); if (mType == DataType::kHALF && !mIsBestAlgoFound) { - std::tie(mAlgoBatchedEx1, mAlgoBatchedEx2) = tuneBatchedGemm(B, S, mNumHeads, mHeadSize); + std::tie(mAlgoBatchedEx1, mAlgoBatchedEx2) = tuneBatchedGemm(B, S, mNumHeads, mHeadSize, mSm); mIsBestAlgoFound = true; gLogVerbose << "QKV Plugin - Selected Algos for batch gemms: " << mAlgoBatchedEx1 << ", " << mAlgoBatchedEx2 diff --git a/plugin/bertQKVToContextPlugin/qkvToContextInt8InterleavedPlugin.cpp b/plugin/bertQKVToContextPlugin/qkvToContextInt8InterleavedPlugin.cpp index 51ab1d56..6c62f49f 100644 --- a/plugin/bertQKVToContextPlugin/qkvToContextInt8InterleavedPlugin.cpp +++ b/plugin/bertQKVToContextPlugin/qkvToContextInt8InterleavedPlugin.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ -#include "qkvToContextInt8InterleavedPlugin.h" #include "NvInfer.h" #include "bertCommon.h" +#include "qkvToContextInt8InterleavedPlugin.h" #include "serialize.hpp" #include @@ -36,8 +36,8 @@ namespace bert namespace { -static const char* QKV_TO_CONTEXT_INTERLEAVED_PLUGIN_VERSION{"3"}; -static const char* QKV_TO_CONTEXT_INTERLEAVED_PLUGIN_NAME{"CustomQKVToContextPluginDynamic"}; +const char* QKV_TO_CONTEXT_INTERLEAVED_PLUGIN_VERSION{"3"}; +const char* QKV_TO_CONTEXT_INTERLEAVED_PLUGIN_NAME{"CustomQKVToContextPluginDynamic"}; } // namespace // Static class fields initialization @@ -80,7 +80,7 @@ QKVToContextInterleavedPlugin::QKVToContextInterleavedPlugin(const std::string n deserialize_value(&data, &length, &mDqProbs); } -int QKVToContextInterleavedPlugin::getSMVersion() const +int QKVToContextInterleavedPlugin::getSMVersion() const noexcept { int device{-1}; CHECK(cudaGetDevice(&device)); @@ -90,7 +90,7 @@ int QKVToContextInterleavedPlugin::getSMVersion() const } // IPluginV2DynamicExt Methods -nvinfer1::IPluginV2DynamicExt* QKVToContextInterleavedPlugin::clone() const +nvinfer1::IPluginV2DynamicExt* QKVToContextInterleavedPlugin::clone() const noexcept { QKVToContextInterleavedPlugin* ret = new QKVToContextInterleavedPlugin(mLayerName, mHiddenSize, mNumHeads, mDqProbs); @@ -100,7 +100,7 @@ nvinfer1::IPluginV2DynamicExt* QKVToContextInterleavedPlugin::clone() const } DimsExprs QKVToContextInterleavedPlugin::getOutputDimensions( - int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) + int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) noexcept { // Input SHAPE is 1x(3*N*H)xTotalx1 (NCHW) // Output SHAPE is 1x(N*H)xTotalx1 @@ -111,12 +111,12 @@ DimsExprs QKVToContextInterleavedPlugin::getOutputDimensions( DimsExprs output(inputs[IIDX]); // output.d[0] = exprBuilder.constant(1); // Divide last dim by three - auto three = exprBuilder.constant(3); + const auto* three = exprBuilder.constant(3); output.d[1] = exprBuilder.operation(DimensionOperation::kFLOOR_DIV, *inputs[IIDX].d[1], *three); return output; } bool QKVToContextInterleavedPlugin::supportsFormatCombination( - int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) + int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept { assert(nbInputs == 3); assert(nbOutputs == 1); @@ -145,54 +145,54 @@ bool QKVToContextInterleavedPlugin::supportsFormatCombination( } void QKVToContextInterleavedPlugin::configurePlugin( - const DynamicPluginTensorDesc* in, int nbInputs, const DynamicPluginTensorDesc* out, int nbOutputs) + const DynamicPluginTensorDesc* in, int nbInputs, const DynamicPluginTensorDesc* out, int nbOutputs) noexcept { } size_t QKVToContextInterleavedPlugin::getWorkspaceSize( - const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const + const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const noexcept { return 0; } // IPluginV2Ext Methods DataType QKVToContextInterleavedPlugin::getOutputDataType( - int index, const nvinfer1::DataType* inputTypes, int nbInputs) const + int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { assert(index == 0); return DataType::kINT8; } // IPluginV2 Methods -const char* QKVToContextInterleavedPlugin::getPluginType() const +const char* QKVToContextInterleavedPlugin::getPluginType() const noexcept { return QKV_TO_CONTEXT_INTERLEAVED_PLUGIN_NAME; } -const char* QKVToContextInterleavedPlugin::getPluginVersion() const +const char* QKVToContextInterleavedPlugin::getPluginVersion() const noexcept { return QKV_TO_CONTEXT_INTERLEAVED_PLUGIN_VERSION; } -int QKVToContextInterleavedPlugin::getNbOutputs() const +int QKVToContextInterleavedPlugin::getNbOutputs() const noexcept { return 1; } -int QKVToContextInterleavedPlugin::initialize() +int QKVToContextInterleavedPlugin::initialize() noexcept { return 0; } -void QKVToContextInterleavedPlugin::terminate() {} +void QKVToContextInterleavedPlugin::terminate() noexcept {} -size_t QKVToContextInterleavedPlugin::getSerializationSize() const +size_t QKVToContextInterleavedPlugin::getSerializationSize() const noexcept { return sizeof(mNumHeads) + sizeof(mHeadSize) + sizeof(mHiddenSize) + sizeof(mSM) + sizeof(mS) + sizeof(mB) + sizeof(mDqProbs); } -void QKVToContextInterleavedPlugin::serialize(void* buffer) const +void QKVToContextInterleavedPlugin::serialize(void* buffer) const noexcept { serialize_value(&buffer, mNumHeads); serialize_value(&buffer, mHeadSize); @@ -203,23 +203,23 @@ void QKVToContextInterleavedPlugin::serialize(void* buffer) const serialize_value(&buffer, mDqProbs); } -void QKVToContextInterleavedPlugin::destroy() +void QKVToContextInterleavedPlugin::destroy() noexcept { delete this; } -void QKVToContextInterleavedPlugin::setPluginNamespace(const char* libNamespace) +void QKVToContextInterleavedPlugin::setPluginNamespace(const char* libNamespace) noexcept { mNamespace = libNamespace; } -const char* QKVToContextInterleavedPlugin::getPluginNamespace() const +const char* QKVToContextInterleavedPlugin::getPluginNamespace() const noexcept { return mNamespace.c_str(); } int QKVToContextInterleavedPlugin::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { const int total = inputDesc[0].dims.d[2]; @@ -255,7 +255,7 @@ int QKVToContextInterleavedPlugin::enqueue(const PluginTensorDesc* inputDesc, co float scaleBmm1 = scaleQkv * scaleQkv * 0.125; // 1 / sqrt(64) float scaleBmm2 = mDqProbs * scaleQkv / scaleCtx; - float scaleSoftmax = 1.f / mDqProbs; + float scaleSoftmax = 1.F / mDqProbs; params.scale_bmm1 = reinterpret_cast(scaleBmm1); params.scale_bmm2 = reinterpret_cast(scaleBmm2); @@ -266,35 +266,38 @@ int QKVToContextInterleavedPlugin::enqueue(const PluginTensorDesc* inputDesc, co params.use_int8_scale_max = true; params.enable_i2f_trick - = -double(1 << 22) * double(scaleBmm2) <= -128.f && double(1 << 22) * double(scaleBmm2) >= 127.f; + = -double(1 << 22) * double(scaleBmm2) <= -128.F && double(1 << 22) * double(scaleBmm2) >= 127.F; mXmmaKernel->run(params, stream); - CHECK(cudaPeekAtLastError()); - return 0; + return cudaPeekAtLastError(); } QKVToContextInterleavedPluginCreator::QKVToContextInterleavedPluginCreator() { + mPluginAttributes.emplace_back(PluginField("hidden_size", nullptr, PluginFieldType::kINT32, 1)); + mPluginAttributes.emplace_back(PluginField("num_heads", nullptr, PluginFieldType::kINT32, 1)); + mPluginAttributes.emplace_back(PluginField("dq_probs", nullptr, PluginFieldType::kFLOAT32, 1)); + mFC.nbFields = mPluginAttributes.size(); mFC.fields = mPluginAttributes.data(); } -const char* QKVToContextInterleavedPluginCreator::getPluginName() const +const char* QKVToContextInterleavedPluginCreator::getPluginName() const noexcept { return QKV_TO_CONTEXT_INTERLEAVED_PLUGIN_NAME; } -const char* QKVToContextInterleavedPluginCreator::getPluginVersion() const +const char* QKVToContextInterleavedPluginCreator::getPluginVersion() const noexcept { return QKV_TO_CONTEXT_INTERLEAVED_PLUGIN_VERSION; } -const PluginFieldCollection* QKVToContextInterleavedPluginCreator::getFieldNames() +const PluginFieldCollection* QKVToContextInterleavedPluginCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2* QKVToContextInterleavedPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2* QKVToContextInterleavedPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { int hiddenSize = 0; int numHeads = 0; @@ -325,17 +328,19 @@ IPluginV2* QKVToContextInterleavedPluginCreator::createPlugin(const char* name, if (hiddenSize <= 0) { gLogError << "QKV: Invalid hiddenSize " << hiddenSize << std::endl; + return nullptr; } if (numHeads <= 0) { gLogError << "QKV: Invalid numHeads " << numHeads << std::endl; + return nullptr; } if (dqProbs < 0) { gLogInfo << "Using default scale factor\n"; - dqProbs = 1.f / 127.f; + dqProbs = 1.F / 127.F; } QKVToContextInterleavedPlugin* p = new QKVToContextInterleavedPlugin(name, hiddenSize, numHeads, dqProbs); @@ -343,19 +348,19 @@ IPluginV2* QKVToContextInterleavedPluginCreator::createPlugin(const char* name, } IPluginV2* QKVToContextInterleavedPluginCreator::deserializePlugin( - const char* name, const void* serialData, size_t serialLength) + const char* name, const void* serialData, size_t serialLength) noexcept { // This object will be deleted when the network is destroyed, which will - // call QKVToContextInterleavedPlugin::destroy() + // call QKVToContextInterleavedPlugin::destroy() noexcept return new QKVToContextInterleavedPlugin(name, serialData, serialLength); } -void QKVToContextInterleavedPluginCreator::setPluginNamespace(const char* libNamespace) +void QKVToContextInterleavedPluginCreator::setPluginNamespace(const char* libNamespace) noexcept { mNamespace = libNamespace; } -const char* QKVToContextInterleavedPluginCreator::getPluginNamespace() const +const char* QKVToContextInterleavedPluginCreator::getPluginNamespace() const noexcept { return mNamespace.c_str(); } diff --git a/plugin/bertQKVToContextPlugin/qkvToContextInt8InterleavedPlugin.h b/plugin/bertQKVToContextPlugin/qkvToContextInt8InterleavedPlugin.h index 060cd9d3..d7cb38eb 100644 --- a/plugin/bertQKVToContextPlugin/qkvToContextInt8InterleavedPlugin.h +++ b/plugin/bertQKVToContextPlugin/qkvToContextInt8InterleavedPlugin.h @@ -45,36 +45,37 @@ public: QKVToContextInterleavedPlugin() = delete; // IPluginV2DynamicExt Methods - nvinfer1::IPluginV2DynamicExt* clone() const override; - nvinfer1::DimsExprs getOutputDimensions( - int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) override; + nvinfer1::IPluginV2DynamicExt* clone() const noexcept override; + nvinfer1::DimsExprs getOutputDimensions(int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, + nvinfer1::IExprBuilder& exprBuilder) noexcept override; bool supportsFormatCombination( - int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) override; + int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept override; void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, - const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) override; + const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) noexcept override; size_t getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int nbInputs, - const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const override; + const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const noexcept override; int enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) override; + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; // IPluginV2Ext Methods - nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const + noexcept override; // IPluginV2 Methods - const char* getPluginType() const override; - const char* getPluginVersion() const override; - int getNbOutputs() const override; - int initialize() override; - void terminate() override; - size_t getSerializationSize() const override; - void serialize(void* buffer) const override; - void destroy() override; - void setPluginNamespace(const char* pluginNamespace) override; - const char* getPluginNamespace() const override; + const char* getPluginType() const noexcept override; + const char* getPluginVersion() const noexcept override; + int getNbOutputs() const noexcept override; + int initialize() noexcept override; + void terminate() noexcept override; + size_t getSerializationSize() const noexcept override; + void serialize(void* buffer) const noexcept override; + void destroy() noexcept override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; + const char* getPluginNamespace() const noexcept override; protected: - void createMHARunner(); - int getSMVersion() const; + void createMHARunner() noexcept; + int getSMVersion() const noexcept; private: const std::string mLayerName; @@ -90,16 +91,6 @@ private: const FusedMultiHeadAttentionXMMAKernelV2* mXmmaKernel; float mDqProbs; - -protected: - // To prevent compiler warnings. - using nvinfer1::IPluginV2DynamicExt::canBroadcastInputAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::configurePlugin; - using nvinfer1::IPluginV2DynamicExt::enqueue; - using nvinfer1::IPluginV2DynamicExt::getOutputDimensions; - using nvinfer1::IPluginV2DynamicExt::getWorkspaceSize; - using nvinfer1::IPluginV2DynamicExt::isOutputBroadcastAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::supportsFormat; }; class QKVToContextInterleavedPluginCreator : public nvinfer1::IPluginCreator @@ -107,19 +98,19 @@ class QKVToContextInterleavedPluginCreator : public nvinfer1::IPluginCreator public: QKVToContextInterleavedPluginCreator(); - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const nvinfer1::PluginFieldCollection* getFieldNames() override; + const nvinfer1::PluginFieldCollection* getFieldNames() noexcept override; - nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) override; + nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) noexcept override; - nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; private: static nvinfer1::PluginFieldCollection mFC; diff --git a/plugin/bertQKVToContextPlugin/qkvToContextPlugin.cpp b/plugin/bertQKVToContextPlugin/qkvToContextPlugin.cpp index 1285f2f8..9b6f018c 100644 --- a/plugin/bertQKVToContextPlugin/qkvToContextPlugin.cpp +++ b/plugin/bertQKVToContextPlugin/qkvToContextPlugin.cpp @@ -38,9 +38,9 @@ namespace bert namespace { -static const char* QKV_TO_CONTEXT_PLUGIN_VERSION{"1"}; -static const char* QKV_TO_CONTEXT_VAR_SEQLEN_PLUGIN_VERSION{"2"}; -static const char* QKV_TO_CONTEXT_PLUGIN_NAME{"CustomQKVToContextPluginDynamic"}; +const char* QKV_TO_CONTEXT_PLUGIN_VERSION{"1"}; +const char* QKV_TO_CONTEXT_VAR_SEQLEN_PLUGIN_VERSION{"2"}; +const char* QKV_TO_CONTEXT_PLUGIN_NAME{"CustomQKVToContextPluginDynamic"}; } // namespace // Static class fields initialization @@ -118,12 +118,12 @@ void QKVToContextPluginDynamic::createMHARunner() if (!unfusedDispatcher.get()) { - unfusedDispatcher.reset(new UnfusedMHARunner(mType, mNumHeads, mHeadSize)); + unfusedDispatcher.reset(new UnfusedMHARunner(mType, mNumHeads, mHeadSize, mSM)); } } // IPluginV2DynamicExt Methods -nvinfer1::IPluginV2DynamicExt* QKVToContextPluginDynamic::clone() const +nvinfer1::IPluginV2DynamicExt* QKVToContextPluginDynamic::clone() const noexcept { gLogVerbose << "QKV Clone" << std::endl; @@ -148,19 +148,19 @@ nvinfer1::IPluginV2DynamicExt* QKVToContextPluginDynamic::clone() const } DimsExprs QKVToContextPluginDynamic::getOutputDimensions( - int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) + int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) noexcept { // Input is BxSx3*N*H, output should be BxSxN*H assert(outputIndex == 0); // Copy over everything DimsExprs output(inputs[IIDX]); // Divide last dim by three - auto three = exprBuilder.constant(3); + const auto* three = exprBuilder.constant(3); output.d[HDIM] = exprBuilder.operation(DimensionOperation::kFLOOR_DIV, *inputs[IIDX].d[HDIM], *three); return output; } bool QKVToContextPluginDynamic::supportsFormatCombination( - int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) + int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept { assert(pos >= 0); assert(pos < 2 + mHasImask); @@ -191,6 +191,16 @@ bool QKVToContextPluginDynamic::supportsFormatCombination( return false; } } + if (mType == DataType::kHALF) + { + if (mSM < kSM_53) + { + gLogError + << "Half-precision floating-point is only supported on compute capability 5.3 and later for plugin " + << QKV_TO_CONTEXT_PLUGIN_NAME << std::endl; + return false; + } + } if (pos == 0) { @@ -264,7 +274,7 @@ bool QKVToContextPluginDynamic::supportsFormatCombination( return false; } void QKVToContextPluginDynamic::configurePlugin( - const DynamicPluginTensorDesc* in, int nbInputs, const DynamicPluginTensorDesc* out, int nbOutputs) + const DynamicPluginTensorDesc* in, int nbInputs, const DynamicPluginTensorDesc* out, int nbOutputs) noexcept { assert(nbInputs == 1 + mHasImask); assert(nbOutputs == 1); @@ -331,7 +341,7 @@ void QKVToContextPluginDynamic::configurePlugin( } size_t QKVToContextPluginDynamic::getWorkspaceSize( - const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const + const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const noexcept { // only unfused kernel need workspace, and we need larger workspace for larger sequence length // we have already setup unfusedDispatcher with max sequence in configurePlugin @@ -342,7 +352,7 @@ size_t QKVToContextPluginDynamic::getWorkspaceSize( // IPluginV2Ext Methods DataType QKVToContextPluginDynamic::getOutputDataType( - int index, const nvinfer1::DataType* inputTypes, int nbInputs) const + int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { assert(index == 0); assert(inputTypes[0] == DataType::kFLOAT || inputTypes[0] == DataType::kHALF || inputTypes[0] == DataType::kINT8); @@ -350,29 +360,29 @@ DataType QKVToContextPluginDynamic::getOutputDataType( } // IPluginV2 Methods -const char* QKVToContextPluginDynamic::getPluginType() const +const char* QKVToContextPluginDynamic::getPluginType() const noexcept { return QKV_TO_CONTEXT_PLUGIN_NAME; } -const char* QKVToContextPluginDynamic::getPluginVersion() const +const char* QKVToContextPluginDynamic::getPluginVersion() const noexcept { return QKV_TO_CONTEXT_PLUGIN_VERSION; } -int QKVToContextPluginDynamic::getNbOutputs() const +int QKVToContextPluginDynamic::getNbOutputs() const noexcept { return 1; } -int QKVToContextPluginDynamic::initialize() +int QKVToContextPluginDynamic::initialize() noexcept { return 0; } -void QKVToContextPluginDynamic::terminate() {} +void QKVToContextPluginDynamic::terminate() noexcept {} -size_t QKVToContextPluginDynamic::getSerializationSize() const +size_t QKVToContextPluginDynamic::getSerializationSize() const noexcept { ASSERT(unfusedDispatcher.get()); return sizeof(mNumHeads) + sizeof(mHeadSize) + sizeof(DataType) + sizeof(mHasImask) + sizeof(mHiddenSize) @@ -380,7 +390,7 @@ size_t QKVToContextPluginDynamic::getSerializationSize() const + unfusedDispatcher->getSerializationSize(); } -void QKVToContextPluginDynamic::serialize(void* buffer) const +void QKVToContextPluginDynamic::serialize(void* buffer) const noexcept { serialize_value(&buffer, mType); serialize_value(&buffer, mNumHeads); @@ -405,23 +415,23 @@ void QKVToContextPluginDynamic::serialize(void* buffer) const } } -void QKVToContextPluginDynamic::destroy() +void QKVToContextPluginDynamic::destroy() noexcept { delete this; } -void QKVToContextPluginDynamic::setPluginNamespace(const char* libNamespace) +void QKVToContextPluginDynamic::setPluginNamespace(const char* libNamespace) noexcept { mNamespace = libNamespace; } -const char* QKVToContextPluginDynamic::getPluginNamespace() const +const char* QKVToContextPluginDynamic::getPluginNamespace() const noexcept { return mNamespace.c_str(); } int QKVToContextPluginDynamic::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { assert(mS == inputDesc->dims.d[SDIM]); assert(mB == inputDesc->dims.d[BDIM]); @@ -446,26 +456,27 @@ QKVToContextPluginDynamicCreator::QKVToContextPluginDynamicCreator() mPluginAttributes.emplace_back(PluginField("num_heads", nullptr, PluginFieldType::kINT32, 1)); mPluginAttributes.emplace_back(PluginField("has_mask", nullptr, PluginFieldType::kINT32, 1)); mPluginAttributes.emplace_back(PluginField("dq_probs", nullptr, PluginFieldType::kFLOAT32, 1)); + mFC.nbFields = mPluginAttributes.size(); mFC.fields = mPluginAttributes.data(); } -const char* QKVToContextPluginDynamicCreator::getPluginName() const +const char* QKVToContextPluginDynamicCreator::getPluginName() const noexcept { return QKV_TO_CONTEXT_PLUGIN_NAME; } -const char* QKVToContextPluginDynamicCreator::getPluginVersion() const +const char* QKVToContextPluginDynamicCreator::getPluginVersion() const noexcept { return QKV_TO_CONTEXT_PLUGIN_VERSION; } -const PluginFieldCollection* QKVToContextPluginDynamicCreator::getFieldNames() +const PluginFieldCollection* QKVToContextPluginDynamicCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2* QKVToContextPluginDynamicCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2* QKVToContextPluginDynamicCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { gLogVerbose << "Creating QKV2ContextPlugin...\n"; @@ -510,16 +521,19 @@ IPluginV2* QKVToContextPluginDynamicCreator::createPlugin(const char* name, cons if (typeId < 0 || typeId > 3) { gLogError << "QKV: Invalid TypeId " << typeId << std::endl; + return nullptr; } if (hiddenSize <= 0) { gLogError << "QKV: Invalid hiddenSize " << hiddenSize << std::endl; + return nullptr; } if (numHeads <= 0) { gLogError << "QKV: Invalid numHeads " << numHeads << std::endl; + return nullptr; } gLogVerbose << "Building the Plugin...\n"; @@ -527,7 +541,7 @@ IPluginV2* QKVToContextPluginDynamicCreator::createPlugin(const char* name, cons if (type == DataType::kINT8 && dqProbs < 0) { gLogInfo << "Using default scale factor\n"; - dqProbs = 1.f / 127.f; + dqProbs = 1.F / 127.F; } QKVToContextPluginDynamic* p = new QKVToContextPluginDynamic(name, type, hiddenSize, numHeads, dqProbs, hasMask); @@ -535,19 +549,19 @@ IPluginV2* QKVToContextPluginDynamicCreator::createPlugin(const char* name, cons } IPluginV2* QKVToContextPluginDynamicCreator::deserializePlugin( - const char* name, const void* serialData, size_t serialLength) + const char* name, const void* serialData, size_t serialLength) noexcept { // This object will be deleted when the network is destroyed, which will // call QKVToContextPluginDynamic::destroy() return new QKVToContextPluginDynamic(name, serialData, serialLength); } -void QKVToContextPluginDynamicCreator::setPluginNamespace(const char* libNamespace) +void QKVToContextPluginDynamicCreator::setPluginNamespace(const char* libNamespace) noexcept { mNamespace = libNamespace; } -const char* QKVToContextPluginDynamicCreator::getPluginNamespace() const +const char* QKVToContextPluginDynamicCreator::getPluginNamespace() const noexcept { return mNamespace.c_str(); } @@ -573,8 +587,7 @@ QKVToContextVarSeqlenPlugin::QKVToContextVarSeqlenPlugin(const std::string name, { // variable sequence length is only supported with the fused MHA kernels // we should not override mS! - assert((mSM == kSM_86 || mSM == kSM_80 || mSM == kSM_75 || mSM == kSM_72) - && (type == DataType::kINT8 || type == DataType::kHALF) + assert((mSM == kSM_86 || mSM == kSM_80 || mSM == kSM_75 || mSM == kSM_72) && (type == DataType::kINT8 || type == DataType::kHALF) && "requesting maxSeqlen not compatible with GPU arch"); // the layout changes: SxB will be a combined \sum_i s_i and hdim will be the 2nd dimension instead of the third mHdim = 1; @@ -626,12 +639,12 @@ void QKVToContextVarSeqlenPlugin::createMHARunner() else { assert(!mUseVarSeqlen); - dispatcher.reset(new UnfusedMHARunner(mType, mNumHeads, mHeadSize)); + dispatcher.reset(new UnfusedMHARunner(mType, mNumHeads, mHeadSize, mSM)); } } // IPluginV2DynamicExt Methods -nvinfer1::IPluginV2DynamicExt* QKVToContextVarSeqlenPlugin::clone() const +nvinfer1::IPluginV2DynamicExt* QKVToContextVarSeqlenPlugin::clone() const noexcept { gLogVerbose << "QKV Clone" << std::endl; @@ -656,26 +669,26 @@ nvinfer1::IPluginV2DynamicExt* QKVToContextVarSeqlenPlugin::clone() const } DimsExprs QKVToContextVarSeqlenPlugin::getOutputDimensions( - int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) + int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) noexcept { // Input is BxSx3*N*H, output should be BxSxN*H assert(outputIndex == 0); // Copy over everything DimsExprs output(inputs[IIDX]); // Divide last dim by three - auto three = exprBuilder.constant(3); + const auto* three = exprBuilder.constant(3); output.d[mHdim] = exprBuilder.operation(DimensionOperation::kFLOOR_DIV, *inputs[IIDX].d[mHdim], *three); return output; } bool QKVToContextVarSeqlenPlugin::supportsFormatCombination( - int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) + int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept { // we only support int8 IO in fused mha runner, and we only support fused mha runner on Turing and Ampere if (mType == DataType::kINT8 && mSM != kSM_86 && mSM != kSM_80 && mSM != kSM_75 && mSM != kSM_72) { - gLogVerbose << "INT8 IO is only supported on Xavier, Turing and Ampere for plugin " - << QKV_TO_CONTEXT_PLUGIN_NAME << std::endl; + gLogVerbose << "INT8 IO is only supported on Xavier, Turing and Ampere for plugin " << QKV_TO_CONTEXT_PLUGIN_NAME + << std::endl; return false; } @@ -762,7 +775,7 @@ bool QKVToContextVarSeqlenPlugin::supportsFormatCombination( } void QKVToContextVarSeqlenPlugin::configurePlugin( - const DynamicPluginTensorDesc* in, int nbInputs, const DynamicPluginTensorDesc* out, int nbOutputs) + const DynamicPluginTensorDesc* in, int nbInputs, const DynamicPluginTensorDesc* out, int nbOutputs) noexcept { assert(nbInputs == 1 + mHasImask + 2 * mUseVarSeqlen); assert(nbOutputs == 1); @@ -807,14 +820,14 @@ void QKVToContextVarSeqlenPlugin::configurePlugin( } size_t QKVToContextVarSeqlenPlugin::getWorkspaceSize( - const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const + const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const noexcept { return this->dispatcher->getWorkspaceSize(); } // IPluginV2Ext Methods DataType QKVToContextVarSeqlenPlugin::getOutputDataType( - int index, const nvinfer1::DataType* inputTypes, int nbInputs) const + int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { assert(index == 0); assert(inputTypes[0] == DataType::kFLOAT || inputTypes[0] == DataType::kHALF || inputTypes[0] == DataType::kINT8); @@ -822,36 +835,36 @@ DataType QKVToContextVarSeqlenPlugin::getOutputDataType( } // IPluginV2 Methods -const char* QKVToContextVarSeqlenPlugin::getPluginType() const +const char* QKVToContextVarSeqlenPlugin::getPluginType() const noexcept { return QKV_TO_CONTEXT_PLUGIN_NAME; } -const char* QKVToContextVarSeqlenPlugin::getPluginVersion() const +const char* QKVToContextVarSeqlenPlugin::getPluginVersion() const noexcept { return QKV_TO_CONTEXT_VAR_SEQLEN_PLUGIN_VERSION; } -int QKVToContextVarSeqlenPlugin::getNbOutputs() const +int QKVToContextVarSeqlenPlugin::getNbOutputs() const noexcept { return 1; } -int QKVToContextVarSeqlenPlugin::initialize() +int QKVToContextVarSeqlenPlugin::initialize() noexcept { return 0; } -void QKVToContextVarSeqlenPlugin::terminate() {} +void QKVToContextVarSeqlenPlugin::terminate() noexcept {} -size_t QKVToContextVarSeqlenPlugin::getSerializationSize() const +size_t QKVToContextVarSeqlenPlugin::getSerializationSize() const noexcept { return sizeof(mNumHeads) + sizeof(mHeadSize) + sizeof(DataType) + sizeof(mHasImask) + sizeof(mHiddenSize) + sizeof(mSM) + sizeof(mS) + sizeof(mB) + sizeof(mDqProbs) + dispatcher->getSerializationSize() + sizeof(mUseVarSeqlen) + sizeof(mHdim); } -void QKVToContextVarSeqlenPlugin::serialize(void* buffer) const +void QKVToContextVarSeqlenPlugin::serialize(void* buffer) const noexcept { serialize_value(&buffer, mType); serialize_value(&buffer, mNumHeads); @@ -868,32 +881,31 @@ void QKVToContextVarSeqlenPlugin::serialize(void* buffer) const dispatcher->serialize(buffer); } -void QKVToContextVarSeqlenPlugin::destroy() +void QKVToContextVarSeqlenPlugin::destroy() noexcept { delete this; } -void QKVToContextVarSeqlenPlugin::setPluginNamespace(const char* libNamespace) +void QKVToContextVarSeqlenPlugin::setPluginNamespace(const char* libNamespace) noexcept { mNamespace = libNamespace; } -const char* QKVToContextVarSeqlenPlugin::getPluginNamespace() const +const char* QKVToContextVarSeqlenPlugin::getPluginNamespace() const noexcept { return mNamespace.c_str(); } int QKVToContextVarSeqlenPlugin::enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, const void* const* inputs, void* const* outputs, void* workspace, - cudaStream_t stream) + cudaStream_t stream) noexcept { if (mUseVarSeqlen) { const int B = inputDesc[1].dims.d[0]; const int maxS = inputDesc[3].dims.d[0]; - ASSERT((maxS <= 384) - && "No implementation for variable sequence length multi-head attention plugin with sequence > 384."); + ASSERT((maxS <= 384) && "No implementation for variable sequence length multi-head attention plugin with sequence > 384."); int S = 384; if (DataType::kHALF == mType && maxS <= 64) @@ -911,10 +923,11 @@ int QKVToContextVarSeqlenPlugin::enqueue(const nvinfer1::PluginTensorDesc* input else if (maxS <= 192) { S = 192; - if (mType == DataType::kHALF) + if(mType == DataType::kHALF) { S = 256; } + } else if (maxS <= 256) { @@ -923,6 +936,7 @@ int QKVToContextVarSeqlenPlugin::enqueue(const nvinfer1::PluginTensorDesc* input this->dispatcher->setup(S, B); this->dispatcher->run(inputDesc, outputDesc, inputs, outputs, workspace, stream); + return cudaGetLastError(); } else { @@ -931,10 +945,8 @@ int QKVToContextVarSeqlenPlugin::enqueue(const nvinfer1::PluginTensorDesc* input const void* maskPtr = mHasImask ? inputs[1] : nullptr; this->dispatcher->run(inputDesc[0], outputDesc[0], inputs[0], maskPtr, outputs[0], workspace, stream); - return 0; + return cudaGetLastError(); } - - return 0; } QKVToContextVarSeqlenPluginCreator::QKVToContextVarSeqlenPluginCreator() @@ -949,22 +961,22 @@ QKVToContextVarSeqlenPluginCreator::QKVToContextVarSeqlenPluginCreator() mFC.fields = mPluginAttributes.data(); } -const char* QKVToContextVarSeqlenPluginCreator::getPluginName() const +const char* QKVToContextVarSeqlenPluginCreator::getPluginName() const noexcept { return QKV_TO_CONTEXT_PLUGIN_NAME; } -const char* QKVToContextVarSeqlenPluginCreator::getPluginVersion() const +const char* QKVToContextVarSeqlenPluginCreator::getPluginVersion() const noexcept { return QKV_TO_CONTEXT_VAR_SEQLEN_PLUGIN_VERSION; } -const PluginFieldCollection* QKVToContextVarSeqlenPluginCreator::getFieldNames() +const PluginFieldCollection* QKVToContextVarSeqlenPluginCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2* QKVToContextVarSeqlenPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2* QKVToContextVarSeqlenPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { gLogVerbose << "Creating QKV2ContextPlugin...\n"; @@ -1016,16 +1028,19 @@ IPluginV2* QKVToContextVarSeqlenPluginCreator::createPlugin(const char* name, co if (typeId < 0 || typeId > 3) { gLogError << "QKV: Invalid TypeId " << typeId << std::endl; + return nullptr; } if (hiddenSize <= 0) { gLogError << "QKV: Invalid hiddenSize " << hiddenSize << std::endl; + return nullptr; } if (numHeads <= 0) { gLogError << "QKV: Invalid numHeads " << numHeads << std::endl; + return nullptr; } gLogVerbose << "Building the Plugin...\n"; @@ -1033,7 +1048,7 @@ IPluginV2* QKVToContextVarSeqlenPluginCreator::createPlugin(const char* name, co if (type == DataType::kINT8 && dqProbs < 0) { gLogInfo << "Using default scale factor\n"; - dqProbs = 1.f / 127.f; + dqProbs = 1.F / 127.F; } QKVToContextVarSeqlenPlugin* p @@ -1042,19 +1057,19 @@ IPluginV2* QKVToContextVarSeqlenPluginCreator::createPlugin(const char* name, co } IPluginV2* QKVToContextVarSeqlenPluginCreator::deserializePlugin( - const char* name, const void* serialData, size_t serialLength) + const char* name, const void* serialData, size_t serialLength) noexcept { // This object will be deleted when the network is destroyed, which will // call QKVToContextVarSeqlenPlugin::destroy() return new QKVToContextVarSeqlenPlugin(name, serialData, serialLength); } -void QKVToContextVarSeqlenPluginCreator::setPluginNamespace(const char* libNamespace) +void QKVToContextVarSeqlenPluginCreator::setPluginNamespace(const char* libNamespace) noexcept { mNamespace = libNamespace; } -const char* QKVToContextVarSeqlenPluginCreator::getPluginNamespace() const +const char* QKVToContextVarSeqlenPluginCreator::getPluginNamespace() const noexcept { return mNamespace.c_str(); } diff --git a/plugin/bertQKVToContextPlugin/qkvToContextPlugin.h b/plugin/bertQKVToContextPlugin/qkvToContextPlugin.h index 43b3f9d8..6a0001d2 100644 --- a/plugin/bertQKVToContextPlugin/qkvToContextPlugin.h +++ b/plugin/bertQKVToContextPlugin/qkvToContextPlugin.h @@ -76,8 +76,8 @@ public: const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) = 0; - virtual size_t getSerializationSize() const; - virtual void serialize(void* buffer) const; + virtual size_t getSerializationSize() const noexcept; + virtual void serialize(void* buffer) const noexcept; virtual void deserialize(const void* data, size_t length); virtual size_t getWorkspaceSize() const = 0; @@ -129,32 +129,33 @@ public: QKVToContextPluginDynamic() = delete; // IPluginV2DynamicExt Methods - nvinfer1::IPluginV2DynamicExt* clone() const override; - nvinfer1::DimsExprs getOutputDimensions( - int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) override; + nvinfer1::IPluginV2DynamicExt* clone() const noexcept override; + nvinfer1::DimsExprs getOutputDimensions(int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, + nvinfer1::IExprBuilder& exprBuilder) noexcept override; bool supportsFormatCombination( - int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) override; + int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept override; void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, - const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) override; + const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) noexcept override; size_t getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int nbInputs, - const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const override; + const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const noexcept override; int enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) override; + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; // IPluginV2Ext Methods - nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const + noexcept override; // IPluginV2 Methods - const char* getPluginType() const override; - const char* getPluginVersion() const override; - int getNbOutputs() const override; - int initialize() override; - void terminate() override; - size_t getSerializationSize() const override; - void serialize(void* buffer) const override; - void destroy() override; - void setPluginNamespace(const char* pluginNamespace) override; - const char* getPluginNamespace() const override; + const char* getPluginType() const noexcept override; + const char* getPluginVersion() const noexcept override; + int getNbOutputs() const noexcept override; + int initialize() noexcept override; + void terminate() noexcept override; + size_t getSerializationSize() const noexcept override; + void serialize(void* buffer) const noexcept override; + void destroy() noexcept override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; + const char* getPluginNamespace() const noexcept override; protected: void createMHARunner(); @@ -177,18 +178,7 @@ private: int mNumHeads; bool mHasImask; nvinfer1::DataType mType; - float mDqProbs; - -protected: - // To prevent compiler warnings. - using nvinfer1::IPluginV2DynamicExt::canBroadcastInputAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::configurePlugin; - using nvinfer1::IPluginV2DynamicExt::enqueue; - using nvinfer1::IPluginV2DynamicExt::getOutputDimensions; - using nvinfer1::IPluginV2DynamicExt::getWorkspaceSize; - using nvinfer1::IPluginV2DynamicExt::isOutputBroadcastAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::supportsFormat; }; class QKVToContextPluginDynamicCreator : public nvinfer1::IPluginCreator @@ -196,19 +186,19 @@ class QKVToContextPluginDynamicCreator : public nvinfer1::IPluginCreator public: QKVToContextPluginDynamicCreator(); - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const nvinfer1::PluginFieldCollection* getFieldNames() override; + const nvinfer1::PluginFieldCollection* getFieldNames() noexcept override; - nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) override; + nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) noexcept override; - nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; private: static nvinfer1::PluginFieldCollection mFC; @@ -229,32 +219,33 @@ public: QKVToContextVarSeqlenPlugin() = delete; // IPluginV2DynamicExt Methods - nvinfer1::IPluginV2DynamicExt* clone() const override; - nvinfer1::DimsExprs getOutputDimensions( - int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) override; + nvinfer1::IPluginV2DynamicExt* clone() const noexcept override; + nvinfer1::DimsExprs getOutputDimensions(int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, + nvinfer1::IExprBuilder& exprBuilder) noexcept override; bool supportsFormatCombination( - int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) override; + int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept override; void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, - const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) override; + const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) noexcept override; size_t getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int nbInputs, - const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const override; + const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const noexcept override; int enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) override; + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; // IPluginV2Ext Methods - nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const + noexcept override; // IPluginV2 Methods - const char* getPluginType() const override; - const char* getPluginVersion() const override; - int getNbOutputs() const override; - int initialize() override; - void terminate() override; - size_t getSerializationSize() const override; - void serialize(void* buffer) const override; - void destroy() override; - void setPluginNamespace(const char* pluginNamespace) override; - const char* getPluginNamespace() const override; + const char* getPluginType() const noexcept override; + const char* getPluginVersion() const noexcept override; + int getNbOutputs() const noexcept override; + int initialize() noexcept override; + void terminate() noexcept override; + size_t getSerializationSize() const noexcept override; + void serialize(void* buffer) const noexcept override; + void destroy() noexcept override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; + const char* getPluginNamespace() const noexcept override; protected: void createMHARunner(); @@ -278,16 +269,6 @@ private: int mHdim; bool mUseVarSeqlen; - -protected: - // To prevent compiler warnings. - using nvinfer1::IPluginV2DynamicExt::canBroadcastInputAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::configurePlugin; - using nvinfer1::IPluginV2DynamicExt::enqueue; - using nvinfer1::IPluginV2DynamicExt::getOutputDimensions; - using nvinfer1::IPluginV2DynamicExt::getWorkspaceSize; - using nvinfer1::IPluginV2DynamicExt::isOutputBroadcastAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::supportsFormat; }; class QKVToContextVarSeqlenPluginCreator : public nvinfer1::IPluginCreator @@ -295,19 +276,19 @@ class QKVToContextVarSeqlenPluginCreator : public nvinfer1::IPluginCreator public: QKVToContextVarSeqlenPluginCreator(); - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const nvinfer1::PluginFieldCollection* getFieldNames() override; + const nvinfer1::PluginFieldCollection* getFieldNames() noexcept override; - nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) override; + nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) noexcept override; - nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; private: static nvinfer1::PluginFieldCollection mFC; @@ -318,7 +299,7 @@ private: class UnfusedMHARunner : public MHARunner { public: - UnfusedMHARunner(const nvinfer1::DataType type, const int numHeads, const int headSize); + UnfusedMHARunner(const nvinfer1::DataType type, const int numHeads, const int headSize, const int smVersion); virtual ~UnfusedMHARunner(); virtual void setup(const int S, const int B) override; @@ -331,8 +312,8 @@ public: size_t getWorkspaceSize() const override; - size_t getSerializationSize() const override; - void serialize(void* buffer) const override; + size_t getSerializationSize() const noexcept override; + void serialize(void* buffer) const noexcept override; void deserialize(const void* data, size_t length) override; bool isValid(int s) const override; @@ -341,6 +322,7 @@ private: int mAlgoBatchedEx1; int mAlgoBatchedEx2; cublasHandle_t mCublas; + int mSm; }; class FusedMHARunnerFP16 : public MHARunner diff --git a/plugin/common/bboxUtils.h b/plugin/common/bboxUtils.h index 88b0a0d4..ff5686d7 100644 --- a/plugin/common/bboxUtils.h +++ b/plugin/common/bboxUtils.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_BBOX_UTILS_H #define TRT_BBOX_UTILS_H diff --git a/plugin/common/bertCommon.h b/plugin/common/bertCommon.h index cab8f989..88128218 100644 --- a/plugin/common/bertCommon.h +++ b/plugin/common/bertCommon.h @@ -44,6 +44,8 @@ constexpr uint32_t BDIM = 1; // batch dimension constexpr uint32_t SDIM = 0; // seq len dimension constexpr uint32_t HDIM = 2; // hidden dimension +constexpr int32_t kSM_53 = 53; +constexpr int32_t kSM_70 = 70; constexpr int32_t kSM_72 = 72; constexpr int32_t kSM_75 = 75; constexpr int32_t kSM_80 = 80; @@ -107,7 +109,7 @@ inline int getMHAMaskPackedSize(int smVersion, nvinfer1::DataType dataType, int return packedSize; } -inline unsigned int getElementSize(nvinfer1::DataType t) +inline uint32_t getElementSize(nvinfer1::DataType t) noexcept { switch (t) { @@ -117,7 +119,6 @@ inline unsigned int getElementSize(nvinfer1::DataType t) case nvinfer1::DataType::kBOOL: case nvinfer1::DataType::kINT8: return 1; } - throw std::runtime_error("Invalid DataType."); return 0; } @@ -362,7 +363,7 @@ struct WeightsWithOwnership : public nvinfer1::Weights } } - void convertAndCopy(const char*& srcBuf, size_t count, nvinfer1::DataType type) + void convertAndCopy(const char*& srcBuf, size_t count, nvinfer1::DataType type) noexcept { this->type = type; this->count = count; diff --git a/plugin/common/checkMacrosPlugin.h b/plugin/common/checkMacrosPlugin.h index 83ed64d2..fe778c3b 100644 --- a/plugin/common/checkMacrosPlugin.h +++ b/plugin/common/checkMacrosPlugin.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef CHECK_MACROS_PLUGIN_H #define CHECK_MACROS_PLUGIN_H @@ -55,8 +54,7 @@ class LogStream : public std::ostream Buf buffer; public: - LogStream() - : std::ostream(&buffer){}; + LogStream() : std::ostream(&buffer) {}; }; extern LogStream gLogError; @@ -84,10 +82,7 @@ public: { } virtual void log(std::ostream& logStream) const; - void setMessage(const char* msg) - { - message = msg; - } + void setMessage(const char* msg) { message = msg; } protected: const char* file{nullptr}; @@ -125,6 +120,11 @@ public: } }; + +inline void caughtError(const std::exception& e) +{ + gLogError << e.what() << std::endl; +} } // namespace plugin } // namespace nvinfer1 @@ -150,24 +150,24 @@ public: } \ } -#define API_CHECK_WEIGHTS(Name) \ - API_CHECK((Name).values != nullptr); \ - API_CHECK((Name).count > 0); \ +#define API_CHECK_WEIGHTS(Name) \ + API_CHECK((Name).values != nullptr); \ + API_CHECK((Name).count > 0); \ API_CHECK(int((Name).type) >= 0 && int((Name).type) < EnumMax()); -#define API_CHECK_WEIGHTS0(Name) \ - API_CHECK((Name).count >= 0); \ - API_CHECK((Name).count > 0 ? ((Name).values != nullptr) : ((Name).values == nullptr)); \ +#define API_CHECK_WEIGHTS0(Name) \ + API_CHECK((Name).count >= 0); \ + API_CHECK((Name).count > 0 ? ((Name).values != nullptr) : ((Name).values == nullptr)); \ API_CHECK(int((Name).type) >= 0 && int((Name).type) < EnumMax()); -#define API_CHECK_WEIGHTS_RETVAL(Name, retval) \ - API_CHECK_RETVAL((Name).values != nullptr, retval); \ - API_CHECK_RETVAL((Name).count > 0, retval); \ +#define API_CHECK_WEIGHTS_RETVAL(Name, retval) \ + API_CHECK_RETVAL((Name).values != nullptr, retval); \ + API_CHECK_RETVAL((Name).count > 0, retval); \ API_CHECK_RETVAL(int((Name).type) >= 0 && int((Name).type) < EnumMax(), retval); -#define API_CHECK_WEIGHTS0_RETVAL(Name, retval) \ - API_CHECK_RETVAL((Name).count >= 0, retval); \ - API_CHECK_RETVAL((Name).count > 0 ? ((Name).values != nullptr) : ((Name).values == nullptr), retval); \ +#define API_CHECK_WEIGHTS0_RETVAL(Name, retval) \ + API_CHECK_RETVAL((Name).count >= 0, retval); \ + API_CHECK_RETVAL((Name).count > 0 ? ((Name).values != nullptr) : ((Name).values == nullptr), retval); \ API_CHECK_RETVAL(int((Name).type) >= 0 && int((Name).type) < EnumMax(), retval); #define API_CHECK_NULL(param) API_CHECK((param) != nullptr) @@ -178,6 +178,26 @@ public: #define API_CHECK_ENUM_RANGE_RETVAL(Type, val, retval) \ API_CHECK_RETVAL(int(val) >= 0 && int(val) < EnumMax(), retval) +#define CHECK_CUDA(call) \ + do \ + { \ + cudaError_t status = call; \ + if (status != cudaSuccess) \ + { \ + return status; \ + } \ + } while (0) + +#define CHECK_CUDNN(call) \ + do \ + { \ + cudnnStatus_t status = call; \ + if (status != CUDNN_STATUS_SUCCESS) \ + { \ + return status; \ + } \ + } while (0) + #define CUBLASASSERTMSG(status_, msg) \ { \ auto s_ = status_; \ diff --git a/plugin/common/common.cuh b/plugin/common/common.cuh index afecf56b..03e1c468 100644 --- a/plugin/common/common.cuh +++ b/plugin/common/common.cuh @@ -438,9 +438,9 @@ __device__ inline float myExp(const float x) return __expf(x); } -static inline __device__ uint32_t float4_to_char4(float x, - float y, - float z, +static inline __device__ uint32_t float4_to_char4(float x, + float y, + float z, float w) { uint32_t dst; #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 720 diff --git a/plugin/common/cub_helper.h b/plugin/common/cub_helper.h index d0742a7a..7e3dc962 100644 --- a/plugin/common/cub_helper.h +++ b/plugin/common/cub_helper.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "kernel.h" template size_t cubSortPairsWorkspaceSize(int num_items, int num_segments) diff --git a/plugin/common/cudaDriverWrapper.cpp b/plugin/common/cudaDriverWrapper.cpp index 5728a65b..cbef9876 100644 --- a/plugin/common/cudaDriverWrapper.cpp +++ b/plugin/common/cudaDriverWrapper.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #define CUDA_LIB_NAME "cuda" #if defined(_WIN32) @@ -33,8 +32,9 @@ #include "cudaDriverWrapper.h" #include "plugin.h" +#include +#include #include -#include using namespace nvinfer1; @@ -43,24 +43,24 @@ CUDADriverWrapper::CUDADriverWrapper() handle = dllOpen(CUDA_LIB_NAME); ASSERT(handle != nullptr); - auto load_sym = [](void* handle, const char* name) { + auto load_sym = [](void* handle, const char *name) { void* ret = dllGetSym(handle, name); ASSERT(ret != nullptr); return ret; }; - *(void**) (&_cuGetErrorName) = load_sym(handle, "cuGetErrorName"); - *(void**) (&_cuFuncSetAttribute) = load_sym(handle, "cuFuncSetAttribute"); - *(void**) (&_cuLinkComplete) = load_sym(handle, "cuLinkComplete"); - *(void**) (&_cuModuleUnload) = load_sym(handle, "cuModuleUnload"); - *(void**) (&_cuLinkDestroy) = load_sym(handle, "cuLinkDestroy"); - *(void**) (&_cuModuleLoadData) = load_sym(handle, "cuModuleLoadData"); - *(void**) (&_cuLinkCreate) = load_sym(handle, "cuLinkCreate_v2"); - *(void**) (&_cuModuleGetFunction) = load_sym(handle, "cuModuleGetFunction"); - *(void**) (&_cuLinkAddFile) = load_sym(handle, "cuLinkAddFile_v2"); - *(void**) (&_cuLinkAddData) = load_sym(handle, "cuLinkAddData_v2"); - *(void**) (&_cuLaunchCooperativeKernel) = load_sym(handle, "cuLaunchCooperativeKernel"); - *(void**) (&_cuLaunchKernel) = load_sym(handle, "cuLaunchKernel"); + *(void**)(&_cuGetErrorName) = load_sym(handle, "cuGetErrorName"); + *(void**)(&_cuFuncSetAttribute) = load_sym(handle, "cuFuncSetAttribute"); + *(void**)(&_cuLinkComplete) = load_sym(handle, "cuLinkComplete"); + *(void**)(&_cuModuleUnload) = load_sym(handle, "cuModuleUnload"); + *(void**)(&_cuLinkDestroy) = load_sym(handle, "cuLinkDestroy"); + *(void**)(&_cuModuleLoadData) = load_sym(handle, "cuModuleLoadData"); + *(void**)(&_cuLinkCreate) = load_sym(handle, "cuLinkCreate_v2"); + *(void**)(&_cuModuleGetFunction) = load_sym(handle, "cuModuleGetFunction"); + *(void**)(&_cuLinkAddFile) = load_sym(handle, "cuLinkAddFile_v2"); + *(void**)(&_cuLinkAddData) = load_sym(handle, "cuLinkAddData_v2"); + *(void**)(&_cuLaunchCooperativeKernel) = load_sym(handle, "cuLaunchCooperativeKernel"); + *(void**)(&_cuLaunchKernel) = load_sym(handle, "cuLaunchKernel"); } CUDADriverWrapper::~CUDADriverWrapper() @@ -99,7 +99,7 @@ CUresult CUDADriverWrapper::cuModuleLoadData(CUmodule* module, const void* image } CUresult CUDADriverWrapper::cuLinkCreate( - unsigned int numOptions, CUjit_option* options, void** optionValues, CUlinkState* stateOut) const + uint32_t numOptions, CUjit_option* options, void** optionValues, CUlinkState* stateOut) const { return (*_cuLinkCreate)(numOptions, options, optionValues, stateOut); } @@ -109,29 +109,29 @@ CUresult CUDADriverWrapper::cuModuleGetFunction(CUfunction* hfunc, CUmodule hmod return (*_cuModuleGetFunction)(hfunc, hmod, name); } -CUresult CUDADriverWrapper::cuLinkAddFile(CUlinkState state, CUjitInputType type, const char* path, - unsigned int numOptions, CUjit_option* options, void** optionValues) const +CUresult CUDADriverWrapper::cuLinkAddFile(CUlinkState state, CUjitInputType type, const char* path, uint32_t numOptions, + CUjit_option* options, void** optionValues) const { return (*_cuLinkAddFile)(state, type, path, numOptions, options, optionValues); } CUresult CUDADriverWrapper::cuLinkAddData(CUlinkState state, CUjitInputType type, void* data, size_t size, - const char* name, unsigned int numOptions, CUjit_option* options, void** optionValues) const + const char* name, uint32_t numOptions, CUjit_option* options, void** optionValues) const { return (*_cuLinkAddData)(state, type, data, size, name, numOptions, options, optionValues); } -CUresult CUDADriverWrapper::cuLaunchCooperativeKernel(CUfunction f, unsigned int gridDimX, unsigned int gridDimY, - unsigned int gridDimZ, unsigned int blockDimX, unsigned int blockDimY, unsigned int blockDimZ, - unsigned int sharedMemBytes, CUstream hStream, void** kernelParams) const +CUresult CUDADriverWrapper::cuLaunchCooperativeKernel(CUfunction f, uint32_t gridDimX, uint32_t gridDimY, + uint32_t gridDimZ, uint32_t blockDimX, uint32_t blockDimY, uint32_t blockDimZ, uint32_t sharedMemBytes, + CUstream hStream, void** kernelParams) const { return (*_cuLaunchCooperativeKernel)( f, gridDimX, gridDimY, gridDimZ, blockDimX, blockDimY, blockDimZ, sharedMemBytes, hStream, kernelParams); } -CUresult CUDADriverWrapper::cuLaunchKernel(CUfunction f, unsigned int gridDimX, unsigned int gridDimY, - unsigned int gridDimZ, unsigned int blockDimX, unsigned int blockDimY, unsigned int blockDimZ, - unsigned int sharedMemBytes, CUstream hStream, void** kernelParams, void** extra) const +CUresult CUDADriverWrapper::cuLaunchKernel(CUfunction f, uint32_t gridDimX, uint32_t gridDimY, uint32_t gridDimZ, + uint32_t blockDimX, uint32_t blockDimY, uint32_t blockDimZ, uint32_t sharedMemBytes, CUstream hStream, + void** kernelParams, void** extra) const { return (*_cuLaunchKernel)( f, gridDimX, gridDimY, gridDimZ, blockDimX, blockDimY, blockDimZ, sharedMemBytes, hStream, kernelParams, extra); diff --git a/plugin/common/cudaDriverWrapper.h b/plugin/common/cudaDriverWrapper.h index 6e03000e..2dd3e23d 100644 --- a/plugin/common/cudaDriverWrapper.h +++ b/plugin/common/cudaDriverWrapper.h @@ -17,6 +17,7 @@ #ifndef CUDA_DRIVER_WRAPPER_H #define CUDA_DRIVER_WRAPPER_H +#include #include #include @@ -46,24 +47,23 @@ public: CUresult cuModuleLoadData(CUmodule* module, const void* image) const; - CUresult cuLinkCreate( - unsigned int numOptions, CUjit_option* options, void** optionValues, CUlinkState* stateOut) const; + CUresult cuLinkCreate(uint32_t numOptions, CUjit_option* options, void** optionValues, CUlinkState* stateOut) const; CUresult cuModuleGetFunction(CUfunction* hfunc, CUmodule hmod, const char* name) const; - CUresult cuLinkAddFile(CUlinkState state, CUjitInputType type, const char* path, unsigned int numOptions, + CUresult cuLinkAddFile(CUlinkState state, CUjitInputType type, const char* path, uint32_t numOptions, CUjit_option* options, void** optionValues) const; CUresult cuLinkAddData(CUlinkState state, CUjitInputType type, void* data, size_t size, const char* name, - unsigned int numOptions, CUjit_option* options, void** optionValues) const; + uint32_t numOptions, CUjit_option* options, void** optionValues) const; - CUresult cuLaunchCooperativeKernel(CUfunction f, unsigned int gridDimX, unsigned int gridDimY, - unsigned int gridDimZ, unsigned int blockDimX, unsigned int blockDimY, unsigned int blockDimZ, - unsigned int sharedMemBytes, CUstream hStream, void** kernelParams) const; + CUresult cuLaunchCooperativeKernel(CUfunction f, uint32_t gridDimX, uint32_t gridDimY, uint32_t gridDimZ, + uint32_t blockDimX, uint32_t blockDimY, uint32_t blockDimZ, uint32_t sharedMemBytes, CUstream hStream, + void** kernelParams) const; - CUresult cuLaunchKernel(CUfunction f, unsigned int gridDimX, unsigned int gridDimY, unsigned int gridDimZ, - unsigned int blockDimX, unsigned int blockDimY, unsigned int blockDimZ, unsigned int sharedMemBytes, - CUstream hStream, void** kernelParams, void** extra) const; + CUresult cuLaunchKernel(CUfunction f, uint32_t gridDimX, uint32_t gridDimY, uint32_t gridDimZ, uint32_t blockDimX, + uint32_t blockDimY, uint32_t blockDimZ, uint32_t sharedMemBytes, CUstream hStream, void** kernelParams, + void** extra) const; private: void* handle; @@ -80,9 +80,9 @@ private: CUlinkState, CUjitInputType, void*, size_t, const char*, unsigned int, CUjit_option*, void**); CUresult (*_cuLaunchCooperativeKernel)(CUfunction, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, CUstream, void**); - CUresult (*_cuLaunchKernel)(CUfunction f, unsigned int gridDimX, unsigned int gridDimY, unsigned int gridDimZ, - unsigned int blockDimX, unsigned int blockDimY, unsigned int blockDimZ, unsigned int sharedMemBytes, - CUstream hStream, void** kernelParams, void** extra); + CUresult (*_cuLaunchKernel)(CUfunction f, uint32_t gridDimX, uint32_t gridDimY, uint32_t gridDimZ, + uint32_t blockDimX, uint32_t blockDimY, uint32_t blockDimZ, uint32_t sharedMemBytes, CUstream hStream, + void** kernelParams, void** extra); }; inline void cuErrCheck_(CUresult stat, const CUDADriverWrapper& wrap, const char* file, int line) diff --git a/plugin/common/kernels/kernel.h b/plugin/common/kernels/kernel.h index 45cbee0a..b6e4f261 100644 --- a/plugin/common/kernels/kernel.h +++ b/plugin/common/kernels/kernel.h @@ -91,6 +91,8 @@ size_t normalizePluginWorkspaceSize(bool acrossSpatial, int C, int H, int W); pluginStatus_t normalizeInference(cudaStream_t stream, cublasHandle_t handle, bool acrossSpatial, bool channelShared, int N, int C, int H, int W, float eps, const void* scale, const void* inputData, void* outputData, void* workspace); +pluginStatus_t scatterNDInference(cudaStream_t stream, int* outputDims, int nOutputDims, int sliceRank, int nRows, int rowSize, int CopySize, int sizeOfElementInBytes, const void* index, const void* updates, const void* data, void* output, void* workspace); + pluginStatus_t priorBoxInference(cudaStream_t stream, PriorBoxParameters param, int H, int W, int numPriors, int numAspectRatios, const void* minSize, const void* maxSize, const void* aspectRatios, void* outputData); diff --git a/plugin/common/kernels/maskRCNNKernels.h b/plugin/common/kernels/maskRCNNKernels.h index 2b6fd19a..3408df33 100644 --- a/plugin/common/kernels/maskRCNNKernels.h +++ b/plugin/common/kernels/maskRCNNKernels.h @@ -100,7 +100,7 @@ struct RefineDetectionWorkSpace nvinfer1::DimsHW sortClassScoreDims; nvinfer1::DimsHW sortClassLabelDims; nvinfer1::DimsHW sortClassSampleIdxDims; - nvinfer1::Dims sortClassValidCountDims = {1, {1, 0}, {nvinfer1::DimensionType::kINDEX}}; + nvinfer1::Dims sortClassValidCountDims = {1, {1, 0}}; nvinfer1::DimsHW sortClassPosDims; nvinfer1::DimsHW sortNMSMarkDims; @@ -132,7 +132,7 @@ struct ProposalWorkSpace nvinfer1::DimsHW sortClassScoreDims; nvinfer1::DimsHW sortClassLabelDims; nvinfer1::DimsHW sortClassSampleIdxDims; - nvinfer1::Dims sortClassValidCountDims = {1, {1, 0}, {nvinfer1::DimensionType::kINDEX}}; + nvinfer1::Dims sortClassValidCountDims = {1, {1, 0}}; nvinfer1::DimsHW sortClassPosDims; nvinfer1::DimsHW sortNMSMarkDims; @@ -167,7 +167,7 @@ struct MultilevelProposeROIWorkSpace nvinfer1::DimsHW sortClassScoreDims; nvinfer1::DimsHW sortClassLabelDims; nvinfer1::DimsHW sortClassSampleIdxDims; - nvinfer1::Dims sortClassValidCountDims = {1, {1, 0}, {nvinfer1::DimensionType::kINDEX}}; + nvinfer1::Dims sortClassValidCountDims = {1, {1, 0}}; nvinfer1::DimsHW sortClassPosDims; nvinfer1::DimsHW sortNMSMarkDims; diff --git a/plugin/common/kernels/reducedMathPlugin.h b/plugin/common/kernels/reducedMathPlugin.h index 12bb8b99..d047b6c7 100644 --- a/plugin/common/kernels/reducedMathPlugin.h +++ b/plugin/common/kernels/reducedMathPlugin.h @@ -30,7 +30,7 @@ namespace plugin namespace detail { -void find_divisor(int denom, unsigned int& mul_coeff, unsigned int& shift_coeff); +void findDivisor(int denom, unsigned int& mul_coeff, unsigned int& shift_coeff); __host__ __device__ __forceinline__ unsigned int umulhi(unsigned int x, unsigned int y) { @@ -58,7 +58,7 @@ public: __host__ __forceinline__ reduced_divisor(int _y) : y(_y) { - detail::find_divisor(y, mul_coeff, shift_coeff); + detail::findDivisor(y, mul_coeff, shift_coeff); } __host__ __device__ __forceinline__ reduced_divisor(unsigned _mul_coeff, unsigned _shift_coeff, int _y) : mul_coeff(_mul_coeff) @@ -68,7 +68,7 @@ public: } __host__ __device__ __forceinline__ int div(int x) const { - // if dividing by 1, then find_divisor wouldn't have worked because + // if dividing by 1, then findDivisor wouldn't have worked because // mul_coeff would have had to be 2^32, which can't be represented, // so we have to special case that one. return (y != 1) ? detail::umulhi((unsigned int) x, mul_coeff) >> shift_coeff : x; diff --git a/plugin/common/nmsUtils.h b/plugin/common/nmsUtils.h index 71baf547..1434bcb6 100644 --- a/plugin/common/nmsUtils.h +++ b/plugin/common/nmsUtils.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_NMS_UTILS_H #define TRT_NMS_UTILS_H diff --git a/plugin/common/plugin.h b/plugin/common/plugin.h index d2a2e87e..27a1fb7b 100644 --- a/plugin/common/plugin.h +++ b/plugin/common/plugin.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_PLUGIN_H #define TRT_PLUGIN_H #include "checkMacrosPlugin.h" @@ -45,15 +44,9 @@ namespace plugin class BasePlugin : public IPluginV2 { protected: - void setPluginNamespace(const char* libNamespace) override - { - mNamespace = libNamespace; - } + void setPluginNamespace(const char* libNamespace) noexcept override { mNamespace = libNamespace; } - const char* getPluginNamespace() const override - { - return mNamespace.c_str(); - } + const char* getPluginNamespace() const noexcept override { return mNamespace.c_str(); } std::string mNamespace; }; @@ -61,12 +54,12 @@ protected: class BaseCreator : public IPluginCreator { public: - void setPluginNamespace(const char* libNamespace) override + void setPluginNamespace(const char* libNamespace) noexcept override { mNamespace = libNamespace; } - const char* getPluginNamespace() const override + const char* getPluginNamespace() const noexcept override { return mNamespace.c_str(); } diff --git a/plugin/common/pluginLogging.h b/plugin/common/pluginLogging.h deleted file mode 100644 index 91ee254d..00000000 --- a/plugin/common/pluginLogging.h +++ /dev/null @@ -1,529 +0,0 @@ -/* - * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. - * - * 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. - */ - -#ifndef PLUGIN_LOGGING_H -#define PLUGIN_LOGGING_H - -#include "NvInferRuntimeCommon.h" -#include -#include -#include -#include -#include -#include -#include -#include - -using Severity = nvinfer1::ILogger::Severity; - -class LogStreamConsumerBuffer : public std::stringbuf -{ -public: - LogStreamConsumerBuffer(std::ostream& stream, const std::string& prefix, bool shouldLog) - : mOutput(stream) - , mPrefix(prefix) - , mShouldLog(shouldLog) - { - } - - LogStreamConsumerBuffer(LogStreamConsumerBuffer&& other) - : mOutput(other.mOutput) - { - } - - ~LogStreamConsumerBuffer() - { - // std::streambuf::pbase() gives a pointer to the beginning of the buffered part of the output sequence - // std::streambuf::pptr() gives a pointer to the current position of the output sequence - // if the pointer to the beginning is not equal to the pointer to the current position, - // call putOutput() to log the output to the stream - if (pbase() != pptr()) - { - putOutput(); - } - } - - // synchronizes the stream buffer and returns 0 on success - // synchronizing the stream buffer consists of inserting the buffer contents into the stream, - // resetting the buffer and flushing the stream - virtual int sync() - { - putOutput(); - return 0; - } - - void putOutput() - { - if (mShouldLog) - { - // prepend timestamp - std::time_t timestamp = std::time(nullptr); - tm* tm_local = std::localtime(×tamp); - std::cout << "["; - std::cout << std::setw(2) << std::setfill('0') << 1 + tm_local->tm_mon << "/"; - std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_mday << "/"; - std::cout << std::setw(4) << std::setfill('0') << 1900 + tm_local->tm_year << "-"; - std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_hour << ":"; - std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_min << ":"; - std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_sec << "] "; - // std::stringbuf::str() gets the string contents of the buffer - // insert the buffer contents pre-appended by the appropriate prefix into the stream - mOutput << mPrefix << str(); - // set the buffer to empty - str(""); - // flush the stream - mOutput.flush(); - } - } - - void setShouldLog(bool shouldLog) - { - mShouldLog = shouldLog; - } - -private: - std::ostream& mOutput; - std::string mPrefix; - bool mShouldLog; -}; - -//! -//! \class LogStreamConsumerBase -//! \brief Convenience object used to initialize LogStreamConsumerBuffer before std::ostream in LogStreamConsumer -//! -class LogStreamConsumerBase -{ -public: - LogStreamConsumerBase(std::ostream& stream, const std::string& prefix, bool shouldLog) - : mBuffer(stream, prefix, shouldLog) - { - } - -protected: - std::mutex mLogMutex; - LogStreamConsumerBuffer mBuffer; -}; - -//! -//! \class LogStreamConsumer -//! \brief Convenience object used to facilitate use of C++ stream syntax when logging messages. -//! Order of base classes is LogStreamConsumerBase and then std::ostream. -//! This is because the LogStreamConsumerBase class is used to initialize the LogStreamConsumerBuffer member field -//! in LogStreamConsumer and then the address of the buffer is passed to std::ostream. -//! This is necessary to prevent the address of an uninitialized buffer from being passed to std::ostream. -//! Please do not change the order of the parent classes. -//! -class LogStreamConsumer : protected LogStreamConsumerBase, public std::ostream -{ -public: - //! \brief Creates a LogStreamConsumer which logs messages with level severity. - //! Reportable severity determines if the messages are severe enough to be logged. - LogStreamConsumer(Severity reportableSeverity, Severity severity) - : LogStreamConsumerBase(severityOstream(severity), severityPrefix(severity), severity <= reportableSeverity) - , std::ostream(&mBuffer) // links the stream buffer with the stream - , mShouldLog(severity <= reportableSeverity) - , mSeverity(severity) - { - } - - LogStreamConsumer(LogStreamConsumer&& other) - : LogStreamConsumerBase(severityOstream(other.mSeverity), severityPrefix(other.mSeverity), other.mShouldLog) - , std::ostream(&mBuffer) // links the stream buffer with the stream - , mShouldLog(other.mShouldLog) - , mSeverity(other.mSeverity) - { - } - - void setReportableSeverity(Severity reportableSeverity) - { - mShouldLog = mSeverity <= reportableSeverity; - mBuffer.setShouldLog(mShouldLog); - } - - std::mutex& getMutex() - { - return mLogMutex; - } - -private: - static std::ostream& severityOstream(Severity severity) - { - return severity >= Severity::kINFO ? std::cout : std::cerr; - } - - static std::string severityPrefix(Severity severity) - { - switch (severity) - { - case Severity::kINTERNAL_ERROR: return "[F] "; - case Severity::kERROR: return "[E] "; - case Severity::kWARNING: return "[W] "; - case Severity::kINFO: return "[I] "; - case Severity::kVERBOSE: return "[V] "; - default: assert(0); return ""; - } - } - - bool mShouldLog; - Severity mSeverity; -}; - -// Use mutex to protect multi-stream write to buffer -template -LogStreamConsumer& operator<<(LogStreamConsumer& logger, const T& obj) -{ - std::lock_guard guard(logger.getMutex()); - auto& os = static_cast(logger); - os << obj; - return logger; -} - -// Special handling std::endl -inline LogStreamConsumer& operator<<(LogStreamConsumer& logger, std::ostream& (*f)(std::ostream&) ) -{ - std::lock_guard guard(logger.getMutex()); - auto& os = static_cast(logger); - os << f; - return logger; -} - -//! \class Logger -//! -//! \brief Class which manages logging of TensorRT tools and samples -//! -//! \details This class provides a common interface for TensorRT tools and samples to log information to the console, -//! and supports logging two types of messages: -//! -//! - Debugging messages with an associated severity (info, warning, error, or internal error/fatal) -//! - Test pass/fail messages -//! -//! The advantage of having all samples use this class for logging as opposed to emitting directly to stdout/stderr is -//! that the logic for controlling the verbosity and formatting of sample output is centralized in one location. -//! -//! In the future, this class could be extended to support dumping test results to a file in some standard format -//! (for example, JUnit XML), and providing additional metadata (e.g. timing the duration of a test run). -//! -//! TODO: For backwards compatibility with existing samples, this class inherits directly from the nvinfer1::ILogger -//! interface, which is problematic since there isn't a clean separation between messages coming from the TensorRT -//! library and messages coming from the sample. -//! -//! In the future (once all samples are updated to use Logger::getTRTLogger() to access the ILogger) we can refactor the -//! class to eliminate the inheritance and instead make the nvinfer1::ILogger implementation a member of the Logger -//! object. - -class Logger : public nvinfer1::ILogger -{ -public: - Logger(Severity severity = Severity::kWARNING) - : mReportableSeverity(severity) - { - } - - //! - //! \enum TestResult - //! \brief Represents the state of a given test - //! - enum class TestResult - { - kRUNNING, //!< The test is running - kPASSED, //!< The test passed - kFAILED, //!< The test failed - kWAIVED //!< The test was waived - }; - - //! - //! \brief Forward-compatible method for retrieving the nvinfer::ILogger associated with this Logger - //! \return The nvinfer1::ILogger associated with this Logger - //! - //! TODO Once all samples are updated to use this method to register the logger with TensorRT, - //! we can eliminate the inheritance of Logger from ILogger - //! - nvinfer1::ILogger& getTRTLogger() - { - return *this; - } - - //! - //! \brief Implementation of the nvinfer1::ILogger::log() virtual method - //! - //! Note samples should not be calling this function directly; it will eventually go away once we eliminate the - //! inheritance from nvinfer1::ILogger - //! - void log(Severity severity, const char* msg) override - { - LogStreamConsumer(mReportableSeverity, severity) << "[TRT] " << std::string(msg) << std::endl; - } - - //! - //! \brief Method for controlling the verbosity of logging output - //! - //! \param severity The logger will only emit messages that have severity of this level or higher. - //! - void setReportableSeverity(Severity severity) - { - mReportableSeverity = severity; - } - - //! - //! \brief Opaque handle that holds logging information for a particular test - //! - //! This object is an opaque handle to information used by the Logger to print test results. - //! The sample must call Logger::defineTest() in order to obtain a TestAtom that can be used - //! with Logger::reportTest{Start,End}(). - //! - class TestAtom - { - public: - TestAtom(TestAtom&&) = default; - - private: - friend class Logger; - - TestAtom(bool started, const std::string& name, const std::string& cmdline) - : mStarted(started) - , mName(name) - , mCmdline(cmdline) - { - } - - bool mStarted; - std::string mName; - std::string mCmdline; - }; - - //! - //! \brief Define a test for logging - //! - //! \param[in] name The name of the test. This should be a string starting with - //! "TensorRT" and containing dot-separated strings containing - //! the characters [A-Za-z0-9_]. - //! For example, "TensorRT.sample_googlenet" - //! \param[in] cmdline The command line used to reproduce the test - // - //! \return a TestAtom that can be used in Logger::reportTest{Start,End}(). - //! - static TestAtom defineTest(const std::string& name, const std::string& cmdline) - { - return TestAtom(false, name, cmdline); - } - - //! - //! \brief A convenience overloaded version of defineTest() that accepts an array of command-line arguments - //! as input - //! - //! \param[in] name The name of the test - //! \param[in] argc The number of command-line arguments - //! \param[in] argv The array of command-line arguments (given as C strings) - //! - //! \return a TestAtom that can be used in Logger::reportTest{Start,End}(). - static TestAtom defineTest(const std::string& name, int argc, char const* const* argv) - { - auto cmdline = genCmdlineString(argc, argv); - return defineTest(name, cmdline); - } - - //! - //! \brief Report that a test has started. - //! - //! \pre reportTestStart() has not been called yet for the given testAtom - //! - //! \param[in] testAtom The handle to the test that has started - //! - static void reportTestStart(TestAtom& testAtom) - { - reportTestResult(testAtom, TestResult::kRUNNING); - assert(!testAtom.mStarted); - testAtom.mStarted = true; - } - - //! - //! \brief Report that a test has ended. - //! - //! \pre reportTestStart() has been called for the given testAtom - //! - //! \param[in] testAtom The handle to the test that has ended - //! \param[in] result The result of the test. Should be one of TestResult::kPASSED, - //! TestResult::kFAILED, TestResult::kWAIVED - //! - static void reportTestEnd(const TestAtom& testAtom, TestResult result) - { - assert(result != TestResult::kRUNNING); - assert(testAtom.mStarted); - reportTestResult(testAtom, result); - } - - static int reportPass(const TestAtom& testAtom) - { - reportTestEnd(testAtom, TestResult::kPASSED); - return EXIT_SUCCESS; - } - - static int reportFail(const TestAtom& testAtom) - { - reportTestEnd(testAtom, TestResult::kFAILED); - return EXIT_FAILURE; - } - - static int reportWaive(const TestAtom& testAtom) - { - reportTestEnd(testAtom, TestResult::kWAIVED); - return EXIT_SUCCESS; - } - - static int reportTest(const TestAtom& testAtom, bool pass) - { - return pass ? reportPass(testAtom) : reportFail(testAtom); - } - - Severity getReportableSeverity() const - { - return mReportableSeverity; - } - -private: - //! - //! \brief returns an appropriate string for prefixing a log message with the given severity - //! - static const char* severityPrefix(Severity severity) - { - switch (severity) - { - case Severity::kINTERNAL_ERROR: return "[F] "; - case Severity::kERROR: return "[E] "; - case Severity::kWARNING: return "[W] "; - case Severity::kINFO: return "[I] "; - case Severity::kVERBOSE: return "[V] "; - default: assert(0); return ""; - } - } - - //! - //! \brief returns an appropriate string for prefixing a test result message with the given result - //! - static const char* testResultString(TestResult result) - { - switch (result) - { - case TestResult::kRUNNING: return "RUNNING"; - case TestResult::kPASSED: return "PASSED"; - case TestResult::kFAILED: return "FAILED"; - case TestResult::kWAIVED: return "WAIVED"; - default: assert(0); return ""; - } - } - - //! - //! \brief returns an appropriate output stream (cout or cerr) to use with the given severity - //! - static std::ostream& severityOstream(Severity severity) - { - return severity >= Severity::kINFO ? std::cout : std::cerr; - } - - //! - //! \brief method that implements logging test results - //! - static void reportTestResult(const TestAtom& testAtom, TestResult result) - { - severityOstream(Severity::kINFO) << "&&&& " << testResultString(result) << " " << testAtom.mName << " # " - << testAtom.mCmdline << std::endl; - } - - //! - //! \brief generate a command line string from the given (argc, argv) values - //! - static std::string genCmdlineString(int argc, char const* const* argv) - { - std::stringstream ss; - for (int i = 0; i < argc; i++) - { - if (i > 0) - ss << " "; - ss << argv[i]; - } - return ss.str(); - } - - Severity mReportableSeverity; -}; - -namespace -{ - -//! -//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kVERBOSE -//! -//! Example usage: -//! -//! LOG_VERBOSE(logger) << "hello world" << std::endl; -//! -inline LogStreamConsumer LOG_VERBOSE(const Logger& logger) -{ - return LogStreamConsumer(logger.getReportableSeverity(), Severity::kVERBOSE); -} - -//! -//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kINFO -//! -//! Example usage: -//! -//! LOG_INFO(logger) << "hello world" << std::endl; -//! -inline LogStreamConsumer LOG_INFO(const Logger& logger) -{ - return LogStreamConsumer(logger.getReportableSeverity(), Severity::kINFO); -} - -//! -//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kWARNING -//! -//! Example usage: -//! -//! LOG_WARN(logger) << "hello world" << std::endl; -//! -inline LogStreamConsumer LOG_WARN(const Logger& logger) -{ - return LogStreamConsumer(logger.getReportableSeverity(), Severity::kWARNING); -} - -//! -//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kERROR -//! -//! Example usage: -//! -//! LOG_ERROR(logger) << "hello world" << std::endl; -//! -inline LogStreamConsumer LOG_ERROR(const Logger& logger) -{ - return LogStreamConsumer(logger.getReportableSeverity(), Severity::kERROR); -} - -//! -//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kINTERNAL_ERROR -// ("fatal" severity) -//! -//! Example usage: -//! -//! LOG_FATAL(logger) << "hello world" << std::endl; -//! -inline LogStreamConsumer LOG_FATAL(const Logger& logger) -{ - return LogStreamConsumer(logger.getReportableSeverity(), Severity::kINTERNAL_ERROR); -} - -} // anonymous namespace - -#endif // PLUGIN_LOGGING_H diff --git a/plugin/common/reducedMathPlugin.cpp b/plugin/common/reducedMathPlugin.cpp index 5208dfca..21577fa0 100644 --- a/plugin/common/reducedMathPlugin.cpp +++ b/plugin/common/reducedMathPlugin.cpp @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +#include namespace nvinfer1 { namespace plugin @@ -46,7 +46,8 @@ int find_log_2(int x, bool round_up = false) return a; } -void find_divisor(int denom, unsigned int& mul_coeff, unsigned int& shift_coeff) +void findDivisor(int denom, + unsigned int& mul_coeff, unsigned int& shift_coeff) { if (denom == 0) { @@ -74,8 +75,8 @@ void find_divisor(int denom, unsigned int& mul_coeff, unsigned int& shift_coeff) // Once we've picked Y, then X [our mul_coeff value] is simply Y/D, rounding up, // and we save shift_coeff as whatever further shift we have to do beyond // what the umulhi() implies. - unsigned int p = 31 + find_log_2(denom, true); - unsigned int m = ((1ull << p) + (unsigned int) denom - 1) / (unsigned int) denom; + uint32_t p = 31 + find_log_2(denom, true); + uint32_t m = ((1ull << p) + (uint32_t) denom - 1) / (uint32_t) denom; mul_coeff = m; shift_coeff = p - 32; } diff --git a/plugin/common/serialize.hpp b/plugin/common/serialize.hpp index 275071f8..62dc9f1b 100644 --- a/plugin/common/serialize.hpp +++ b/plugin/common/serialize.hpp @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #pragma once -#include #include -#include #include +#include +#include #include using std::cerr; diff --git a/plugin/coordConvACPlugin/coordConvACPlugin.cpp b/plugin/coordConvACPlugin/coordConvACPlugin.cpp index 1ac6ce98..e070cac0 100644 --- a/plugin/coordConvACPlugin/coordConvACPlugin.cpp +++ b/plugin/coordConvACPlugin/coordConvACPlugin.cpp @@ -56,19 +56,19 @@ CoordConvACPlugin::CoordConvACPlugin(const void* data, size_t length) ASSERT(d == a + length); } -int CoordConvACPlugin::getNbOutputs() const +int CoordConvACPlugin::getNbOutputs() const noexcept { return 1; } -int CoordConvACPlugin::initialize() +int CoordConvACPlugin::initialize() noexcept { return STATUS_SUCCESS; } -void CoordConvACPlugin::terminate() {} +void CoordConvACPlugin::terminate() noexcept {} -Dims CoordConvACPlugin::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) +Dims CoordConvACPlugin::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept { // CHW nvinfer1::Dims dimsOutput; @@ -80,18 +80,18 @@ Dims CoordConvACPlugin::getOutputDimensions(int index, const Dims* inputs, int n return dimsOutput; } -size_t CoordConvACPlugin::getWorkspaceSize(int maxBatchSize) const +size_t CoordConvACPlugin::getWorkspaceSize(int maxBatchSize) const noexcept { return 0; } -size_t CoordConvACPlugin::getSerializationSize() const +size_t CoordConvACPlugin::getSerializationSize() const noexcept { // iC, iH, iW, oC, oH, oW return sizeof(int) * 6; } -void CoordConvACPlugin::serialize(void* buffer) const +void CoordConvACPlugin::serialize(void* buffer) const noexcept { char *d = reinterpret_cast(buffer), *a = d; write(d, iC); @@ -105,7 +105,7 @@ void CoordConvACPlugin::serialize(void* buffer) const void CoordConvACPlugin::configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, nvinfer1::PluginFormat format, int maxBatchSize) + const bool* outputIsBroadcast, nvinfer1::PluginFormat format, int maxBatchSize) noexcept { ASSERT(nbInputs == 1); ASSERT(nbOutputs == 1); @@ -121,55 +121,55 @@ void CoordConvACPlugin::configurePlugin(const Dims* inputDims, int nbInputs, con iType = inputTypes[0]; } -bool CoordConvACPlugin::supportsFormat(DataType type, PluginFormat format) const +bool CoordConvACPlugin::supportsFormat(DataType type, PluginFormat format) const noexcept { - return ((type == DataType::kFLOAT || type == DataType::kHALF) && format == PluginFormat::kNCHW); + return ((type == DataType::kFLOAT || type == DataType::kHALF) && format == PluginFormat::kLINEAR); } -const char* CoordConvACPlugin::getPluginType() const +const char* CoordConvACPlugin::getPluginType() const noexcept { return COORDCONV_AC_PLUGIN_NAME; } -const char* CoordConvACPlugin::getPluginVersion() const +const char* CoordConvACPlugin::getPluginVersion() const noexcept { return COORDCONV_AC_PLUGIN_VERSION; } -void CoordConvACPlugin::destroy() +void CoordConvACPlugin::destroy() noexcept { delete this; } -IPluginV2Ext* CoordConvACPlugin::clone() const +IPluginV2Ext* CoordConvACPlugin::clone() const noexcept { auto* plugin = new CoordConvACPlugin(iType, iC, iH, iW, oC, oH, oW); return plugin; } -void CoordConvACPlugin::setPluginNamespace(const char* pluginNamespace) +void CoordConvACPlugin::setPluginNamespace(const char* pluginNamespace) noexcept { mPluginNamespace = pluginNamespace; } -const char* CoordConvACPlugin::getPluginNamespace() const +const char* CoordConvACPlugin::getPluginNamespace() const noexcept { return mPluginNamespace; } nvinfer1::DataType CoordConvACPlugin::getOutputDataType( - int index, const nvinfer1::DataType* inputTypes, int nbInputs) const + int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { return inputTypes[0]; } bool CoordConvACPlugin::isOutputBroadcastAcrossBatch( - int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const + int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept { return false; } -bool CoordConvACPlugin::canBroadcastInputAcrossBatch(int inputIndex) const +bool CoordConvACPlugin::canBroadcastInputAcrossBatch(int inputIndex) const noexcept { return false; } @@ -177,29 +177,29 @@ bool CoordConvACPlugin::canBroadcastInputAcrossBatch(int inputIndex) const // Plugin creator CoordConvACPluginCreator::CoordConvACPluginCreator() {} -const char* CoordConvACPluginCreator::getPluginName() const +const char* CoordConvACPluginCreator::getPluginName() const noexcept { return COORDCONV_AC_PLUGIN_NAME; } -const char* CoordConvACPluginCreator::getPluginVersion() const +const char* CoordConvACPluginCreator::getPluginVersion() const noexcept { return COORDCONV_AC_PLUGIN_VERSION; } -const PluginFieldCollection* CoordConvACPluginCreator::getFieldNames() +const PluginFieldCollection* CoordConvACPluginCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2Ext* CoordConvACPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2Ext* CoordConvACPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { CoordConvACPlugin* plugin = new CoordConvACPlugin(); plugin->setPluginNamespace(mNamespace.c_str()); return plugin; } -IPluginV2Ext* CoordConvACPluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) +IPluginV2Ext* CoordConvACPluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept { CoordConvACPlugin* plugin = new CoordConvACPlugin(serialData, serialLength); plugin->setPluginNamespace(mNamespace.c_str()); diff --git a/plugin/coordConvACPlugin/coordConvACPlugin.cu b/plugin/coordConvACPlugin/coordConvACPlugin.cu index 48a851e5..d3114aee 100644 --- a/plugin/coordConvACPlugin/coordConvACPlugin.cu +++ b/plugin/coordConvACPlugin/coordConvACPlugin.cu @@ -98,7 +98,7 @@ } int CoordConvACPlugin::enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) + int batchSize, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { switch(iType){ case DataType::kFLOAT: diff --git a/plugin/coordConvACPlugin/coordConvACPlugin.h b/plugin/coordConvACPlugin/coordConvACPlugin.h index 50238dd4..0b651075 100644 --- a/plugin/coordConvACPlugin/coordConvACPlugin.h +++ b/plugin/coordConvACPlugin/coordConvACPlugin.h @@ -40,46 +40,46 @@ public: ~CoordConvACPlugin() override = default; - int getNbOutputs() const override; + int getNbOutputs() const noexcept override; - Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) override; + Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept override; - int initialize() override; + int initialize() noexcept override; - void terminate() override; + void terminate() noexcept override; - size_t getWorkspaceSize(int maxBatchSize) const override; + size_t getWorkspaceSize(int maxBatchSize) const noexcept override; - int enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) override; + int enqueue(int batchSize, void const* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept override; - size_t getSerializationSize() const override; + size_t getSerializationSize() const noexcept override; - void serialize(void* buffer) const override; + void serialize(void* buffer) const noexcept override; void configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) override; + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept override; - bool supportsFormat(DataType type, PluginFormat format) const override; + bool supportsFormat(DataType type, PluginFormat format) const noexcept override; - const char* getPluginType() const override; + const char* getPluginType() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - void destroy() override; + void destroy() noexcept override; - IPluginV2Ext* clone() const override; + IPluginV2Ext* clone() const noexcept override; - nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputType, int nbInputs) const override; + nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputType, int nbInputs) const noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; - bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const override; + bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept override; - bool canBroadcastInputAcrossBatch(int inputIndex) const override; + bool canBroadcastInputAcrossBatch(int inputIndex) const noexcept override; private: DataType iType; @@ -96,15 +96,15 @@ public: ~CoordConvACPluginCreator() override = default; - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const PluginFieldCollection* getFieldNames() override; + const PluginFieldCollection* getFieldNames() noexcept override; - IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) override; + IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; - IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; private: static PluginFieldCollection mFC; diff --git a/plugin/cropAndResizePlugin/cropAndResizePlugin.cpp b/plugin/cropAndResizePlugin/cropAndResizePlugin.cpp index 86c6e0b0..cffac819 100644 --- a/plugin/cropAndResizePlugin/cropAndResizePlugin.cpp +++ b/plugin/cropAndResizePlugin/cropAndResizePlugin.cpp @@ -31,8 +31,8 @@ using nvinfer1::plugin::CropAndResizeDynamicPluginCreator; // plugin specific constants namespace { -static const char* CROP_AND_RESIZE_PLUGIN_VERSION{"1"}; -static const char* CROP_AND_RESIZE_PLUGIN_NAMES[] = {"CropAndResize", "CropAndResizeDynamic"}; +const char* CROP_AND_RESIZE_PLUGIN_VERSION{"1"}; +const char* CROP_AND_RESIZE_PLUGIN_NAMES[] = {"CropAndResize", "CropAndResizeDynamic"}; } // namespace // Static class fields initialization @@ -56,19 +56,19 @@ T readFromBuffer(const char*& buffer) return val; } -CropAndResizePlugin::CropAndResizePlugin(int crop_width, int crop_height) noexcept +CropAndResizePlugin::CropAndResizePlugin(int crop_width, int crop_height) : mCropWidth(crop_width) , mCropHeight(crop_height) { } -CropAndResizeDynamicPlugin::CropAndResizeDynamicPlugin(int crop_width, int crop_height) noexcept +CropAndResizeDynamicPlugin::CropAndResizeDynamicPlugin(int crop_width, int crop_height) : mCropWidth(crop_width) , mCropHeight(crop_height) { } -CropAndResizePlugin::CropAndResizePlugin(const void* serial_buf, size_t serial_size) noexcept +CropAndResizePlugin::CropAndResizePlugin(const void* serial_buf, size_t serial_size) { const char* d = reinterpret_cast(serial_buf); const char* a = d; @@ -81,7 +81,7 @@ CropAndResizePlugin::CropAndResizePlugin(const void* serial_buf, size_t serial_s ASSERT(d == a + sizeof(size_t) * 6); } -CropAndResizeDynamicPlugin::CropAndResizeDynamicPlugin(const void* serial_buf, size_t serial_size) noexcept +CropAndResizeDynamicPlugin::CropAndResizeDynamicPlugin(const void* serial_buf, size_t serial_size) { const char* d = reinterpret_cast(serial_buf); const char* a = d; @@ -95,7 +95,7 @@ CropAndResizeDynamicPlugin::CropAndResizeDynamicPlugin(const void* serial_buf, s } CropAndResizePlugin::CropAndResizePlugin( - int crop_width, int crop_height, int depth, int input_width, int input_height, int max_box_num) noexcept + int crop_width, int crop_height, int depth, int input_width, int input_height, int max_box_num) : mCropWidth(crop_width) , mCropHeight(crop_height) , mDepth(depth) @@ -106,7 +106,7 @@ CropAndResizePlugin::CropAndResizePlugin( } CropAndResizeDynamicPlugin::CropAndResizeDynamicPlugin( - int crop_width, int crop_height, int depth, int input_width, int input_height, int max_box_num) noexcept + int crop_width, int crop_height, int depth, int input_width, int input_height, int max_box_num) : mCropWidth(crop_width) , mCropHeight(crop_height) , mDepth(depth) @@ -116,9 +116,9 @@ CropAndResizeDynamicPlugin::CropAndResizeDynamicPlugin( { } -CropAndResizePlugin::~CropAndResizePlugin() noexcept {} +CropAndResizePlugin::~CropAndResizePlugin() {} -CropAndResizeDynamicPlugin::~CropAndResizeDynamicPlugin() noexcept {} +CropAndResizeDynamicPlugin::~CropAndResizeDynamicPlugin() {} const char* CropAndResizePlugin::getPluginType() const noexcept { @@ -160,7 +160,7 @@ Dims CropAndResizePlugin::getOutputDimensions(int index, const Dims* inputs, int int height = mCropHeight; int width = mCropWidth; int roi_batch = inputs[1].d[0]; - return DimsNCHW(roi_batch, channels, height, width); + return Dims4(roi_batch, channels, height, width); } DimsExprs CropAndResizeDynamicPlugin::getOutputDimensions( @@ -191,30 +191,45 @@ int CropAndResizeDynamicPlugin::initialize() noexcept } int CropAndResizePlugin::enqueue( - int batchSize, const void* const* inputs, void** outputs, void*, cudaStream_t stream) noexcept + int batchSize, const void* const* inputs, void* const* outputs, void*, cudaStream_t stream) noexcept { - int status = -1; - // Our plugin outputs only one tensor - void* output = outputs[0]; - // Launch CUDA kernel wrapper and save its return value - status = cropAndResizeInference(stream, mDepth * mInputHeight * mInputWidth * batchSize, inputs[0], inputs[1], - batchSize, mInputHeight, mInputWidth, mNumboxes, mCropHeight, mCropWidth, mDepth, output); - ASSERT(status == STATUS_SUCCESS); - return status; + try + { + int status = -1; + // Our plugin outputs only one tensor + void* output = outputs[0]; + // Launch CUDA kernel wrapper and save its return value + status = cropAndResizeInference(stream, mDepth * mInputHeight * mInputWidth * batchSize, inputs[0], inputs[1], + batchSize, mInputHeight, mInputWidth, mNumboxes, mCropHeight, mCropWidth, mDepth, output); + return status; + } + catch (const std::exception& e) + { + caughtError(e); + } + return -1; } int CropAndResizeDynamicPlugin::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { - int status = -1; - // Our plugin outputs only one tensor - void* output = outputs[0]; - // Launch CUDA kernel wrapper and save its return value - int batchSize = inputDesc[0].dims.d[0]; - status = cropAndResizeInference(stream, mDepth * mInputHeight * mInputWidth * batchSize, inputs[0], inputs[1], - batchSize, mInputHeight, mInputWidth, mNumboxes, mCropHeight, mCropWidth, mDepth, output); - ASSERT(status == STATUS_SUCCESS); - return status; + try + { + int status = -1; + // Our plugin outputs only one tensor + void* output = outputs[0]; + // Launch CUDA kernel wrapper and save its return value + int batchSize = inputDesc[0].dims.d[0]; + status = cropAndResizeInference(stream, mDepth * mInputHeight * mInputWidth * batchSize, inputs[0], inputs[1], + batchSize, mInputHeight, mInputWidth, mNumboxes, mCropHeight, mCropWidth, mDepth, output); + ASSERT(status == STATUS_SUCCESS); + return status; + } + catch (const std::exception& e) + { + caughtError(e); + } + return -1; } size_t CropAndResizePlugin::getSerializationSize() const noexcept @@ -256,7 +271,7 @@ void CropAndResizeDynamicPlugin::serialize(void* buffer) const noexcept bool CropAndResizePlugin::supportsFormat(DataType type, PluginFormat format) const noexcept { // This plugin only supports ordinary floats, and NCHW input format - if (type == DataType::kFLOAT && format == PluginFormat::kNCHW) + if (type == DataType::kFLOAT && format == PluginFormat::kLINEAR) { return true; } @@ -288,7 +303,7 @@ void CropAndResizePlugin::terminate() noexcept {} void CropAndResizeDynamicPlugin::terminate() noexcept {} -size_t CropAndResizePlugin::getWorkspaceSize(int) const noexcept +size_t CropAndResizePlugin::getWorkspaceSize(int32_t /*maxBatchSize*/) const noexcept { return 0; } @@ -313,28 +328,58 @@ void CropAndResizeDynamicPlugin::destroy() noexcept IPluginV2Ext* CropAndResizePlugin::clone() const noexcept { - IPluginV2Ext* plugin - = new CropAndResizePlugin(mCropWidth, mCropHeight, mDepth, mInputWidth, mInputHeight, mNumboxes); - plugin->setPluginNamespace(mNamespace.c_str()); - return plugin; + try + { + IPluginV2Ext* plugin = new CropAndResizePlugin( + mCropWidth, mCropHeight, mDepth, mInputWidth, mInputHeight, mNumboxes); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } IPluginV2DynamicExt* CropAndResizeDynamicPlugin::clone() const noexcept { - IPluginV2DynamicExt* plugin - = new CropAndResizeDynamicPlugin(mCropWidth, mCropHeight, mDepth, mInputWidth, mInputHeight, mNumboxes); - plugin->setPluginNamespace(mNamespace.c_str()); - return plugin; + try + { + IPluginV2DynamicExt* plugin + = new CropAndResizeDynamicPlugin(mCropWidth, mCropHeight, mDepth, mInputWidth, mInputHeight, mNumboxes); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } void CropAndResizePlugin::setPluginNamespace(const char* libNamespace) noexcept { - mNamespace = libNamespace; + try + { + mNamespace = libNamespace; + } + catch (const std::exception& e) + { + caughtError(e); + } } void CropAndResizeDynamicPlugin::setPluginNamespace(const char* libNamespace) noexcept { - mNamespace = libNamespace; + try + { + mNamespace = libNamespace; + } + catch (const std::exception& e) + { + caughtError(e); + } } const char* CropAndResizePlugin::getPluginNamespace() const noexcept @@ -348,8 +393,7 @@ const char* CropAndResizeDynamicPlugin::getPluginNamespace() const noexcept } // Return the DataType of the plugin output at the requested index. -DataType CropAndResizePlugin::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const - noexcept +DataType CropAndResizePlugin::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { // one outputs ASSERT(index == 0); @@ -381,6 +425,9 @@ void CropAndResizePlugin::configurePlugin(const Dims* inputDims, int nbInputs, c const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept { + ASSERT(inputTypes[0] == DataType::kFLOAT && inputTypes[1] == DataType::kFLOAT && + floatFormat == PluginFormat::kLINEAR); + ASSERT(nbInputs == 2); ASSERT(nbOutputs == 1); mDepth = inputDims[0].d[0]; @@ -408,7 +455,7 @@ void CropAndResizePlugin::attachToContext( // Detach the plugin object from its execution context. void CropAndResizePlugin::detachFromContext() noexcept {} -CropAndResizeBasePluginCreator::CropAndResizeBasePluginCreator() noexcept +CropAndResizeBasePluginCreator::CropAndResizeBasePluginCreator() { mPluginAttributes.clear(); mPluginAttributes.emplace_back(PluginField("crop_width", nullptr, PluginFieldType::kINT32, 1)); @@ -417,12 +464,12 @@ CropAndResizeBasePluginCreator::CropAndResizeBasePluginCreator() noexcept mFC.fields = mPluginAttributes.data(); } -CropAndResizePluginCreator::CropAndResizePluginCreator() noexcept +CropAndResizePluginCreator::CropAndResizePluginCreator() { mPluginName = CROP_AND_RESIZE_PLUGIN_NAMES[0]; } -CropAndResizeDynamicPluginCreator::CropAndResizeDynamicPluginCreator() noexcept +CropAndResizeDynamicPluginCreator::CropAndResizeDynamicPluginCreator() { mPluginName = CROP_AND_RESIZE_PLUGIN_NAMES[1]; } @@ -444,73 +491,106 @@ const PluginFieldCollection* CropAndResizeBasePluginCreator::getFieldNames() noe IPluginV2Ext* CropAndResizePluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { - const PluginField* fields = fc->fields; - int nbFields = fc->nbFields; - int crop_width = 0, crop_height = 0; - - for (int i = 0; i < nbFields; ++i) + try { - ASSERT(fields[i].type == PluginFieldType::kINT32); + const PluginField* fields = fc->fields; + int nbFields = fc->nbFields; + int crop_width = 0, crop_height = 0; - if (!strcmp(fields[i].name, "crop_width")) + for (int i = 0; i < nbFields; ++i) { - crop_width = *(reinterpret_cast(fields[i].data)); + ASSERT(fields[i].type == PluginFieldType::kINT32); + + if (!strcmp(fields[i].name, "crop_width")) + { + crop_width = *(reinterpret_cast(fields[i].data)); + } + + if (!strcmp(fields[i].name, "crop_height")) + { + crop_height = *(reinterpret_cast(fields[i].data)); + } } - if (!strcmp(fields[i].name, "crop_height")) - { - crop_height = *(reinterpret_cast(fields[i].data)); - } + ASSERT(crop_width > 0 && crop_height > 0); + IPluginV2Ext* plugin = new CropAndResizePlugin(crop_width, crop_height); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; } - - ASSERT(crop_width > 0 && crop_height > 0); - IPluginV2Ext* plugin = new CropAndResizePlugin(crop_width, crop_height); - plugin->setPluginNamespace(mNamespace.c_str()); - return plugin; + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } IPluginV2DynamicExt* CropAndResizeDynamicPluginCreator::createPlugin( const char* name, const PluginFieldCollection* fc) noexcept { - const PluginField* fields = fc->fields; - int nbFields = fc->nbFields; - int crop_width = 0, crop_height = 0; - - for (int i = 0; i < nbFields; ++i) + try { - ASSERT(fields[i].type == PluginFieldType::kINT32); + + const PluginField* fields = fc->fields; + int nbFields = fc->nbFields; + int crop_width = 0, crop_height = 0; - if (!strcmp(fields[i].name, "crop_width")) + for (int i = 0; i < nbFields; ++i) { - crop_width = *(reinterpret_cast(fields[i].data)); + ASSERT(fields[i].type == PluginFieldType::kINT32); + + if (!strcmp(fields[i].name, "crop_width")) + { + crop_width = *(reinterpret_cast(fields[i].data)); + } + + if (!strcmp(fields[i].name, "crop_height")) + { + crop_height = *(reinterpret_cast(fields[i].data)); + } } - if (!strcmp(fields[i].name, "crop_height")) - { - crop_height = *(reinterpret_cast(fields[i].data)); - } + ASSERT(crop_width > 0 && crop_height > 0); + IPluginV2DynamicExt* plugin = new CropAndResizeDynamicPlugin(crop_width, crop_height); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; } - - ASSERT(crop_width > 0 && crop_height > 0); - IPluginV2DynamicExt* plugin = new CropAndResizeDynamicPlugin(crop_width, crop_height); - plugin->setPluginNamespace(mNamespace.c_str()); - return plugin; + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } IPluginV2Ext* CropAndResizePluginCreator::deserializePlugin( const char* name, const void* serialData, size_t serialLength) noexcept { - // This object will be deleted when the network is destroyed, - IPluginV2Ext* plugin = new CropAndResizePlugin(serialData, serialLength); - plugin->setPluginNamespace(mNamespace.c_str()); - return plugin; + try + { + // This object will be deleted when the network is destroyed, + IPluginV2Ext* plugin = new CropAndResizePlugin(serialData, serialLength); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } IPluginV2DynamicExt* CropAndResizeDynamicPluginCreator::deserializePlugin( const char* name, const void* serialData, size_t serialLength) noexcept { - // This object will be deleted when the network is destroyed, - IPluginV2DynamicExt* plugin = new CropAndResizeDynamicPlugin(serialData, serialLength); - plugin->setPluginNamespace(mNamespace.c_str()); - return plugin; + try + { + // This object will be deleted when the network is destroyed, + IPluginV2DynamicExt* plugin = new CropAndResizeDynamicPlugin(serialData, serialLength); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } diff --git a/plugin/cropAndResizePlugin/cropAndResizePlugin.h b/plugin/cropAndResizePlugin/cropAndResizePlugin.h index cc8d2cdf..00be7924 100644 --- a/plugin/cropAndResizePlugin/cropAndResizePlugin.h +++ b/plugin/cropAndResizePlugin/cropAndResizePlugin.h @@ -36,15 +36,14 @@ namespace plugin class CropAndResizePlugin : public IPluginV2Ext { public: - CropAndResizePlugin(int crop_width, int crop_height) noexcept; - CropAndResizePlugin( - int crop_width, int crop_height, int depth, int input_width, int input_height, int max_box_num) noexcept; - CropAndResizePlugin(const void* serial_buf, size_t serial_size) noexcept; + CropAndResizePlugin(int crop_width, int crop_height); + CropAndResizePlugin(int crop_width, int crop_height, int depth, int input_width, int input_height, int max_box_num); + CropAndResizePlugin(const void* serial_buf, size_t serial_size); // It doesn't make sense to make CropAndResizePlugin without arguments, so we delete default constructor. - CropAndResizePlugin() noexcept = delete; + CropAndResizePlugin() = delete; - ~CropAndResizePlugin() noexcept override; + ~CropAndResizePlugin() override; int getNbOutputs() const noexcept override; @@ -54,9 +53,9 @@ public: void terminate() noexcept override; - size_t getWorkspaceSize(int) const noexcept override; + size_t getWorkspaceSize(int32_t /*maxBatchSize*/) const noexcept override; - int enqueue(int batchSize, const void* const* inputs, void** outputs, void* workspace, + int enqueue(int batchSize, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; size_t getSerializationSize() const noexcept override; @@ -79,8 +78,7 @@ public: DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept override; - bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const - noexcept override; + bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept override; bool canBroadcastInputAcrossBatch(int inputIndex) const noexcept override; @@ -101,10 +99,9 @@ private: class CropAndResizeDynamicPlugin : public IPluginV2DynamicExt { public: - CropAndResizeDynamicPlugin(int crop_width, int crop_height) noexcept; - CropAndResizeDynamicPlugin( - int crop_width, int crop_height, int depth, int input_width, int input_height, int max_box_num) noexcept; - CropAndResizeDynamicPlugin(const void* serial_buf, size_t serial_size) noexcept; + CropAndResizeDynamicPlugin(int crop_width, int crop_height); + CropAndResizeDynamicPlugin(int crop_width, int crop_height, int depth, int input_width, int input_height, int max_box_num); + CropAndResizeDynamicPlugin(const void* serial_buf, size_t serial_size); // It doesn't make sense to make CropAndResizeDynamicPlugin without arguments, so we delete default constructor. CropAndResizeDynamicPlugin() noexcept = delete; @@ -147,8 +144,8 @@ private: class CropAndResizeBasePluginCreator : public BaseCreator { public: - CropAndResizeBasePluginCreator() noexcept; - ~CropAndResizeBasePluginCreator() noexcept override = default; + CropAndResizeBasePluginCreator(); + ~CropAndResizeBasePluginCreator() override = default; const char* getPluginName() const noexcept override; const char* getPluginVersion() const noexcept override; const PluginFieldCollection* getFieldNames() noexcept override; @@ -162,8 +159,8 @@ protected: class CropAndResizePluginCreator : public CropAndResizeBasePluginCreator { public: - CropAndResizePluginCreator() noexcept; - ~CropAndResizePluginCreator() noexcept override = default; + CropAndResizePluginCreator(); + ~CropAndResizePluginCreator() override = default; IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; }; @@ -171,8 +168,8 @@ public: class CropAndResizeDynamicPluginCreator : public CropAndResizeBasePluginCreator { public: - CropAndResizeDynamicPluginCreator() noexcept; - ~CropAndResizeDynamicPluginCreator() noexcept override = default; + CropAndResizeDynamicPluginCreator(); + ~CropAndResizeDynamicPluginCreator() override = default; IPluginV2DynamicExt* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; IPluginV2DynamicExt* deserializePlugin( const char* name, const void* serialData, size_t serialLength) noexcept override; diff --git a/plugin/detectionLayerPlugin/detectionLayerPlugin.cpp b/plugin/detectionLayerPlugin/detectionLayerPlugin.cpp index 00c102db..f02b8b63 100644 --- a/plugin/detectionLayerPlugin/detectionLayerPlugin.cpp +++ b/plugin/detectionLayerPlugin/detectionLayerPlugin.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "detectionLayerPlugin.h" #include "plugin.h" #include @@ -44,55 +43,71 @@ DetectionLayerPluginCreator::DetectionLayerPluginCreator() mFC.fields = mPluginAttributes.data(); } -const char* DetectionLayerPluginCreator::getPluginName() const +const char* DetectionLayerPluginCreator::getPluginName() const noexcept { return DETECTIONLAYER_PLUGIN_NAME; -}; +} -const char* DetectionLayerPluginCreator::getPluginVersion() const +const char* DetectionLayerPluginCreator::getPluginVersion() const noexcept { return DETECTIONLAYER_PLUGIN_VERSION; -}; +} -const PluginFieldCollection* DetectionLayerPluginCreator::getFieldNames() +const PluginFieldCollection* DetectionLayerPluginCreator::getFieldNames() noexcept { return &mFC; -}; +} -IPluginV2Ext* DetectionLayerPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2Ext* DetectionLayerPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { - const PluginField* fields = fc->fields; - for (int i = 0; i < fc->nbFields; ++i) + try { - const char* attrName = fields[i].name; - if (!strcmp(attrName, "num_classes")) + const PluginField* fields = fc->fields; + for (int i = 0; i < fc->nbFields; ++i) { - assert(fields[i].type == PluginFieldType::kINT32); - mNbClasses = *(static_cast(fields[i].data)); - } - if (!strcmp(attrName, "keep_topk")) - { - assert(fields[i].type == PluginFieldType::kINT32); - mKeepTopK = *(static_cast(fields[i].data)); - } - if (!strcmp(attrName, "score_threshold")) - { - assert(fields[i].type == PluginFieldType::kFLOAT32); - mScoreThreshold = *(static_cast(fields[i].data)); - } - if (!strcmp(attrName, "iou_threshold")) - { - assert(fields[i].type == PluginFieldType::kFLOAT32); - mIOUThreshold = *(static_cast(fields[i].data)); + const char* attrName = fields[i].name; + if (!strcmp(attrName, "num_classes")) + { + assert(fields[i].type == PluginFieldType::kINT32); + mNbClasses = *(static_cast(fields[i].data)); + } + if (!strcmp(attrName, "keep_topk")) + { + assert(fields[i].type == PluginFieldType::kINT32); + mKeepTopK = *(static_cast(fields[i].data)); + } + if (!strcmp(attrName, "score_threshold")) + { + assert(fields[i].type == PluginFieldType::kFLOAT32); + mScoreThreshold = *(static_cast(fields[i].data)); + } + if (!strcmp(attrName, "iou_threshold")) + { + assert(fields[i].type == PluginFieldType::kFLOAT32); + mIOUThreshold = *(static_cast(fields[i].data)); + } } + return new DetectionLayer(mNbClasses, mKeepTopK, mScoreThreshold, mIOUThreshold); } - return new DetectionLayer(mNbClasses, mKeepTopK, mScoreThreshold, mIOUThreshold); -}; + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; +} -IPluginV2Ext* DetectionLayerPluginCreator::deserializePlugin(const char* name, const void* data, size_t length) +IPluginV2Ext* DetectionLayerPluginCreator::deserializePlugin(const char* name, const void* data, size_t length) noexcept { - return new DetectionLayer(data, length); -}; + try + { + return new DetectionLayer(data, length); + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; +} DetectionLayer::DetectionLayer(int num_classes, int keep_topk, float score_threshold, float iou_threshold) : mNbClasses(num_classes) @@ -113,14 +128,14 @@ DetectionLayer::DetectionLayer(int num_classes, int keep_topk, float score_thres mParam.iouThreshold = mIOUThreshold; mType = DataType::kFLOAT; -}; +} -int DetectionLayer::getNbOutputs() const +int DetectionLayer::getNbOutputs() const noexcept { return 1; -}; +} -int DetectionLayer::initialize() +int DetectionLayer::initialize() noexcept { //@Init the mValidCnt and mDecodedBboxes for max batch size std::vector tempValidCnt(mMaxBatchSize, mAnchorsCnt); @@ -131,53 +146,68 @@ int DetectionLayer::initialize() mValidCnt->mPtr, static_cast(tempValidCnt.data()), sizeof(int) * mMaxBatchSize, cudaMemcpyHostToDevice)); return 0; -}; +} -void DetectionLayer::terminate(){}; +void DetectionLayer::terminate() noexcept {} -void DetectionLayer::destroy() +void DetectionLayer::destroy() noexcept { delete this; -}; +} -bool DetectionLayer::supportsFormat(DataType type, PluginFormat format) const +bool DetectionLayer::supportsFormat(DataType type, PluginFormat format) const noexcept { - return (type == DataType::kFLOAT && format == PluginFormat::kNCHW); -}; + return (type == DataType::kFLOAT && format == PluginFormat::kLINEAR); +} -const char* DetectionLayer::getPluginType() const +const char* DetectionLayer::getPluginType() const noexcept { return "DetectionLayer_TRT"; -}; +} -const char* DetectionLayer::getPluginVersion() const +const char* DetectionLayer::getPluginVersion() const noexcept { return "1"; -}; +} -IPluginV2Ext* DetectionLayer::clone() const +IPluginV2Ext* DetectionLayer::clone() const noexcept { - DetectionLayer* plugin = new DetectionLayer(*this); - plugin->setPluginNamespace(mNameSpace.c_str()); - return plugin; -}; + try + { + DetectionLayer* plugin = new DetectionLayer(*this); + plugin->setPluginNamespace(mNameSpace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; +} -void DetectionLayer::setPluginNamespace(const char* libNamespace) +void DetectionLayer::setPluginNamespace(const char* libNamespace) noexcept { - mNameSpace = libNamespace; -}; + try + { + mNameSpace = libNamespace; + } + catch (const std::exception& e) + { + caughtError(e); + } +} -const char* DetectionLayer::getPluginNamespace() const +const char* DetectionLayer::getPluginNamespace() const noexcept { return mNameSpace.c_str(); } -size_t DetectionLayer::getSerializationSize() const +size_t DetectionLayer::getSerializationSize() const noexcept { return sizeof(int) * 2 + sizeof(float) * 2 + sizeof(int) * 2; -}; +} -void DetectionLayer::serialize(void* buffer) const +void DetectionLayer::serialize(void* buffer) const noexcept { char *d = reinterpret_cast(buffer), *a = d; write(d, mNbClasses); @@ -187,7 +217,7 @@ void DetectionLayer::serialize(void* buffer) const write(d, mMaxBatchSize); write(d, mAnchorsCnt); ASSERT(d == a + getSerializationSize()); -}; +} DetectionLayer::DetectionLayer(const void* data, size_t length) { @@ -212,7 +242,7 @@ DetectionLayer::DetectionLayer(const void* data, size_t length) mParam.iouThreshold = mIOUThreshold; mType = DataType::kFLOAT; -}; +} void DetectionLayer::check_valid_inputs(const nvinfer1::Dims* inputs, int nbInputDims) { @@ -226,15 +256,15 @@ void DetectionLayer::check_valid_inputs(const nvinfer1::Dims* inputs, int nbInpu assert(inputs[1].nbDims == 4 && inputs[1].d[1] == mNbClasses); // roi assert(inputs[2].nbDims == 2 && inputs[2].d[1] == 4); -}; +} -size_t DetectionLayer::getWorkspaceSize(int batch_size) const +size_t DetectionLayer::getWorkspaceSize(int batch_size) const noexcept { RefineDetectionWorkSpace refine(batch_size, mAnchorsCnt, mParam, mType); return refine.totalSize; -}; +} -Dims DetectionLayer::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) +Dims DetectionLayer::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept { check_valid_inputs(inputs, nbInputDims); @@ -252,40 +282,46 @@ Dims DetectionLayer::getOutputDimensions(int index, const Dims* inputs, int nbIn } int DetectionLayer::enqueue( - int batch_size, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) + int batch_size, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { + try + { + void* detections = outputs[0]; - void* detections = outputs[0]; + // refine detection + RefineDetectionWorkSpace refDetcWorkspace(batch_size, mAnchorsCnt, mParam, mType); + cudaError_t status = RefineBatchClassNMS(stream, batch_size, mAnchorsCnt, + DataType::kFLOAT, // mType, + mParam, refDetcWorkspace, workspace, + inputs[1], // inputs[InScore] + inputs[0], // inputs[InDelta], + mValidCnt->mPtr, // inputs[InCountValid], + inputs[2], // inputs[ROI] + detections); - // refine detection - RefineDetectionWorkSpace refDetcWorkspace(batch_size, mAnchorsCnt, mParam, mType); - cudaError_t status = RefineBatchClassNMS(stream, batch_size, mAnchorsCnt, - DataType::kFLOAT, // mType, - mParam, refDetcWorkspace, workspace, - inputs[1], // inputs[InScore] - inputs[0], // inputs[InDelta], - mValidCnt->mPtr, // inputs[InCountValid], - inputs[2], // inputs[ROI] - detections); + return status; + } + catch (const std::exception& e) + { + caughtError(e); + } + return -1; +} - assert(status == cudaSuccess); - return status; -}; - -DataType DetectionLayer::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const +DataType DetectionLayer::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { // Only DataType::kFLOAT is acceptable by the plugin layer return DataType::kFLOAT; } // Return true if output tensor is broadcast across a batch. -bool DetectionLayer::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const +bool DetectionLayer::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept { return false; } // Return true if plugin can use input that is broadcast across batch without replication. -bool DetectionLayer::canBroadcastInputAcrossBatch(int inputIndex) const +bool DetectionLayer::canBroadcastInputAcrossBatch(int inputIndex) const noexcept { return false; } @@ -293,7 +329,7 @@ bool DetectionLayer::canBroadcastInputAcrossBatch(int inputIndex) const // Configure the layer with input and output data types. void DetectionLayer::configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept { check_valid_inputs(inputDims, nbInputs); assert(inputDims[0].d[0] == inputDims[1].d[0] && inputDims[1].d[0] == inputDims[2].d[0]); @@ -305,9 +341,9 @@ void DetectionLayer::configurePlugin(const Dims* inputDims, int nbInputs, const // Attach the plugin object to an execution context and grant the plugin the access to some context resource. void DetectionLayer::attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) + cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept { } // Detach the plugin object from its execution context. -void DetectionLayer::detachFromContext() {} +void DetectionLayer::detachFromContext() noexcept {} diff --git a/plugin/detectionLayerPlugin/detectionLayerPlugin.h b/plugin/detectionLayerPlugin/detectionLayerPlugin.h index c25afad9..578dc321 100644 --- a/plugin/detectionLayerPlugin/detectionLayerPlugin.h +++ b/plugin/detectionLayerPlugin/detectionLayerPlugin.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_DETECTION_LAYER_PLUGIN_H #define TRT_DETECTION_LAYER_PLUGIN_H #include @@ -41,51 +40,51 @@ public: ~DetectionLayer() override = default; - int getNbOutputs() const override; + int getNbOutputs() const noexcept override; - Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) override; + Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept override; - int initialize() override; + int initialize() noexcept override; - void terminate() override; + void terminate() noexcept override; - void destroy() override; + void destroy() noexcept override; - size_t getWorkspaceSize(int maxBatchSize) const override; + size_t getWorkspaceSize(int maxBatchSize) const noexcept override; - int enqueue( - int batch_size, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) override; + int enqueue(int batch_size, const void* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept override; - size_t getSerializationSize() const override; + size_t getSerializationSize() const noexcept override; - void serialize(void* buffer) const override; + void serialize(void* buffer) const noexcept override; - bool supportsFormat(DataType type, PluginFormat format) const override; + bool supportsFormat(DataType type, PluginFormat format) const noexcept override; - const char* getPluginType() const override; + const char* getPluginType() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - IPluginV2Ext* clone() const override; + IPluginV2Ext* clone() const noexcept override; - void setPluginNamespace(const char* libNamespace) override; + void setPluginNamespace(const char* libNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; - DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept override; - bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const override; + bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept override; - bool canBroadcastInputAcrossBatch(int inputIndex) const override; + bool canBroadcastInputAcrossBatch(int inputIndex) const noexcept override; void attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) override; + cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept override; void configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) override; + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept override; - void detachFromContext() override; + void detachFromContext() noexcept override; private: void check_valid_inputs(const nvinfer1::Dims* inputs, int nbInputDims); @@ -112,15 +111,15 @@ public: ~DetectionLayerPluginCreator(){}; - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const PluginFieldCollection* getFieldNames() override; + const PluginFieldCollection* getFieldNames() noexcept override; - IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) override; + IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; - IPluginV2Ext* deserializePlugin(const char* name, const void* data, size_t length) override; + IPluginV2Ext* deserializePlugin(const char* name, const void* data, size_t length) noexcept override; private: static PluginFieldCollection mFC; diff --git a/samples/opensource/sampleMovieLens/CMakeLists.txt b/plugin/efficientNMSPlugin/CMakeLists.txt similarity index 70% rename from samples/opensource/sampleMovieLens/CMakeLists.txt rename to plugin/efficientNMSPlugin/CMakeLists.txt index 2177a693..53b70a7e 100644 --- a/samples/opensource/sampleMovieLens/CMakeLists.txt +++ b/plugin/efficientNMSPlugin/CMakeLists.txt @@ -13,10 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # -SET(SAMPLE_SOURCES - sampleMovieLens.cpp -) - -set(SAMPLE_PARSERS "uff") - -include(../../CMakeSamplesTemplate.txt) +file(GLOB SRCS *.cpp) +set(PLUGIN_SOURCES ${PLUGIN_SOURCES} ${SRCS}) +set(PLUGIN_SOURCES ${PLUGIN_SOURCES} PARENT_SCOPE) +file(GLOB CU_SRCS *.cu) +set(PLUGIN_CU_SOURCES ${PLUGIN_CU_SOURCES} ${CU_SRCS}) +set(PLUGIN_CU_SOURCES ${PLUGIN_CU_SOURCES} PARENT_SCOPE) diff --git a/plugin/efficientNMSPlugin/README.md b/plugin/efficientNMSPlugin/README.md new file mode 100644 index 00000000..45ea8939 --- /dev/null +++ b/plugin/efficientNMSPlugin/README.md @@ -0,0 +1,145 @@ +# Efficient NMS Plugin + +#### Table of Contents +- [Description](#description) +- [Structure](#structure) + * [Inputs](#inputs) + * [Box Coding Type](#box-coding-type) + * [Outputs](#outputs) + * [Parameters](#parameters) +- [Algorithm](#algorithm) + * [Process Description](#process-description) + * [Performance Tuning](#performance-tuning) + * [Additional Resources](#additional-resources) +- [License](#license) + +## Description + +This TensorRT plugin implements an efficient algorithm to perform Non Maximum Suppression for object detection networks. + +This plugin is primarily intended for using with EfficientDet on TensorRT, as this network is particularly sensitive to the latencies introduced by slower NMS implementations. However, the plugin is generic enough that it will work correctly for other detections architectures, such as SSD or FasterRCNN. + +## Structure + +### Inputs + +The plugin has two modes of operation, depending on the given input data. The plugin will automatically detect which mode to operate as, depending on the number of inputs it receives, as follows: + +1. **Standard NMS Mode:** Only two input tensors are given, (i) the bounding box coordinates and (ii) the corresponding classification scores for each box. + +2. **Fused Box Decoder Mode:** Three input tensors are given, (i) the raw localization predictions for each box originating directly from the localization head of the network, (ii) the corresponding classification scores originating from the classification head of the network, and (iii) the default anchor box coordinates usually hardcoded as constant tensors in the network. + +Most object detection networks work by generating raw predictions from a "localization head" which adjust the coordinates of standard non-learned anchor coordinates to produce a tighter fitting bounding box. This process is called "box decoding", and it usually involves a large number of element-wise operations to transform the anchors to final box coordinates. As this can involve exponential operations on a large number of anchors, it can be computationally expensive, so this plugin gives the option of fusing the box decoder within the NMS operation which can be done in a far more efficient manner, resulting in lower latency for the network. + +#### Boxes Input +The boxes input has shape `[batch_size, number_boxes, 4]` or `[batch_size, number_boxes, number_classes, 4]`, where the former is in case a single box prediction is produced for all classes such as in EfficientDet or SSD, and the latter is when separate box predictions are generated for each individual class, such as in FasterRCNN. The final dimension represents the four coordinates that define the bounding box prediction. + +For *Standard NMS* mode, this tensor should contain the final box coordinates for each predicted detection. For *Fused Box Decoder* mode, this tensor should have the raw localization predictions. + +#### Scores Input +The scores input has shape `[batch_size, number_boxes, number_classes]`, such that for each anchor box, there are `num_classes` elements with the predicted scores for each candidate class. + +Usually, the score values will have passed through a sigmoid activation function before reaching the NMS operation. However, as an optimization, the pre-sigmoid raw scores can also be provided to the NMS plugin to reduce overall network latency. If raw scores are given, enable the `score_activation` parameter so they are processed accordingly. + +#### Anchors Input (Optional) +Only used in *Fused Box Decoder* mode. It is much more efficient to perform the box decoding steps within this plugin. In this case, the boxes input will be treated as the raw box corrections, and this third input should contain the default anchor/prior box coordinates. + +When used, the anchors input has shape `[1, number_anchors, 4]` or `[batch_size, number_anchors, 4]`, where the former is in case anchors are the same for all images in the batch, and the latter is in case they change for each image -- such as in the box refinement NMS of FasterRCNN's second stage. + +### Box Coding Type +Different object detection networks represent their box coordinate system differently. The two types supported by this plugin are: + +1. **BoxCorners:** The four coordinates represent `[x1, y1, x2, y2]` values, where each x,y pair defines the top-left and bottom-right corners of a bounding box. +2. **BoxCenterSize:** The four coordinates represent `[x, y, w, h]` values, where the x,y pair define the box center location, and the w,h pair define its width and height. + +Note that for NMS purposes, horizontal and vertical coordinates are fully interchangeable. TensorFlow-trained networks, for example, often uses vertical-first coordinates such as `[y1, x1, y2, x2]`, but this coordinate system will work equally well under the BoxCorner coding. Similarly, `[y, x, h, w]` will be properly covered by the BoxCornerSize coding. + +In *Fused Box Decoder* mode, the boxes and anchor tensors should both use the same coding. + +### Outputs + +The following five output are generated: + +- **num_detections:** + This is a `[batch_size, 1]` integer tensor. The last dimension is a scalar indicating the number of valid detections per batch item. It can be less than `keepTopK`. Only the top `num_detections[i]` entries in `nms_boxes[i]`, `nms_scores[i]` and `nms_classes[i]` are valid. + +- **detection_boxes:** + This is a `[batch_size, max_output_boxes, 4]` floating point tensor containing the coordinates of non-max suppressed boxes. The output coordinates will always be in BoxCorner format, regardless of the input code type. + +- **detection_scores:** + This is a `[batch_size, max_output_boxes]` floating point tensor containing the scores for the boxes. + +- **detection_classes:** + This is a `[batch_size, max_output_boxes]` integer tensor containing the classes for the boxes. + +- **detection_indices:** + This is a `[batch_size * max_output_boxes, 3]` integer tensor that contains the selected box indices for each box kept by NMS. The purpose of this output is to mimic the result of the [NonMaxSuppression](https://github.com/onnx/onnx/blob/master/docs/Operators.md#NonMaxSuppression) ONNX op. + +### Parameters + +| Type | Parameter | Description +|----------|--------------------------|-------------------------------------------------------- +|`float` |`score_threshold` * |The scalar threshold for score (low scoring boxes are removed). +|`float` |`iou_threshold` |The scalar threshold for IOU (additional boxes that have high IOU overlap with previously selected boxes are removed). +|`int` |`max_output_boxes` |The maximum number of detections to output per image. +|`int` |`background_class` |The label ID for the background class. If there is no background class, set it to `-1`. +|`bool` |`score_activation` * |Set to true to apply sigmoid activation to the confidence scores during NMS operation. +|`int` |`box_coding` |Coding type used for boxes (and anchors if applicable), 0 = BoxCorner, 1 = BoxCenterSize. + +Parameters marked with a `*` have a non-negligible effect on runtime latency. See the [Performance Tuning](#performance-tuning) section below for more details on how to set them optimally. + +## Algorithm + +### Process Description + +The NMS algorithm in this plugin first filters the scores below the given `scoreThreshold`. This subset of scores is then sorted, and their corresponding boxes are then further filtered out by removing boxes that overlap each other with an IOU above the given `iouThreshold`. + +The algorithm launcher and its relevant CUDA kernels are all defined in the `efficientNMSInference.cu` file. + +Specifically, the NMS algorithm does the following: + +- The scores are filtered with the `score_threshold` parameter to reject any scores below the score threshold, while maintaining indexing to cross-reference these scores to their corresponding box coordinates. This is done with the `EfficientNMSFilter` CUDA kernel. + +- If too many elements are kept, due to a very low (or zero) score threshold, the filter operation can become a bottleneck due to the atomic operations involved. To mitigate this, a fallback kernel `EfficientNMSDenseIndex` is used instead which passes all the score elements densely packed and indexed. This method is heuristically selected only if the score threshold is less than 0.007. + +- The selected scores that remain after filtering are sorted in descending order. The indexing is carefully handled to still maintain score to box relationships after sorting. + +- After sorting, the highest 4096 scores are processed by the `EfficientNMS` CUDA kernel. This algorithm uses the index data maintained throughout the previous steps to find the boxes corresponding to the remaining scores. If the fused box decoder is being used, decoding will happen until this stage, where only the top scoring boxes need to be decoded. + +- The NMS kernel uses an efficient filtering algorithm that largely reduces the number of IOU overlap cross-checks between box pairs. The boxes that survive the IOU filtering finally pass through to the output results. At this stage, the sigmoid activation is applied to only the final remaining scores, if `score_activation` is enabled, thereby greatly reducing the amount of sigmoid calculations required otherwise. + +### Performance Tuning + +The plugin implements a very efficient NMS algorithm which largely reduces the latency of this operation in comparison to other NMS plugins. However, there are certain considerations that can help to better fine tune its performance: + +#### Choosing the Score Threshold + +The algorithm is highly sensitive to the selected `score_threshold` parameter. With a higher threshold, fewer elements need to be processed and so the algorithm runs much faster. Therefore, it's beneficial to always select the highest possible score threshold that fulfills the application requirements. Threshold values lower than approximately 0.01 may cause substantially higher latency. + +#### Using Sigmoid Activation + +Depending on network configuration, it is usually more efficient to provide raw scores (pre-sigmoid) to the NMS plugin scores input, and enable the `score_activation` parameter. Doing so applies a sigmoid activation only to the last `max_output_boxes` selected scores, instead of all the predicted scores, largely reducing the computational cost. + +#### Using the Fused Box Decoder + +When using networks with many anchors, such as EfficientDet or SSD, it may be more efficient to do box decoding within the NMS plugin. For this, pass the raw box predictions as the boxes input, and the default anchor coordinates as the optional third input to the plugin. + +### Additional Resources + +The following resources provide a deeper understanding of the NMS algorithm: + +#### Networks +- [EfficientDet](https://arxiv.org/abs/1911.09070) +- [SSD: Single Shot MultiBox Detector](https://arxiv.org/abs/1512.02325) +- [Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks](https://arxiv.org/abs/1506.01497) +- [Mask R-CNN](https://arxiv.org/abs/1703.06870) + + +#### Documentation +- [NMS algorithm](https://www.coursera.org/lecture/convolutional-neural-networks/non-max-suppression-dvrjH) +- [NonMaxSuppression ONNX Op](https://github.com/onnx/onnx/blob/master/docs/Operators.md#NonMaxSuppression) + +## 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. diff --git a/plugin/efficientNMSPlugin/efficientNMSInference.cu b/plugin/efficientNMSPlugin/efficientNMSInference.cu new file mode 100644 index 00000000..88eb0340 --- /dev/null +++ b/plugin/efficientNMSPlugin/efficientNMSInference.cu @@ -0,0 +1,695 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * + * 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 "bboxUtils.h" +#include "cub/cub.cuh" +#include "cuda_runtime_api.h" + +#include "efficientNMSInference.cuh" +#include "efficientNMSInference.h" + +using namespace nvinfer1; + +template +__device__ float IOU(EfficientNMSParameters param, BoxCorner box1, BoxCorner box2) +{ + // Regardless of the selected box coding, IOU is always performed in BoxCorner coding. + float intersectArea = BoxCorner::intersect(box1, box2).area(); + if (intersectArea <= 0.f) + { + return 0.f; + } + float unionArea = box1.area() + box2.area() - intersectArea; + if (unionArea <= 0.f) + { + return 0.f; + } + return intersectArea / unionArea; +} + +template +__device__ BoxCorner DecodeBoxes(EfficientNMSParameters param, int boxIdx, int anchorIdx, + const Tb* __restrict__ boxesInput, const Tb* __restrict__ anchorsInput) +{ + // The inputs will be in the selected coding format, as well as the decoding function. But the decoded box + // will always be returned as BoxCorner. + Tb box = boxesInput[boxIdx]; + box.reorder(); + if (!param.boxDecoder) + { + return BoxCorner(box); + } + Tb anchor = anchorsInput[anchorIdx]; + anchor.reorder(); + return BoxCorner(box.decode(anchor)); +} + +template +__device__ void MapNMSData(EfficientNMSParameters param, int idx, int imageIdx, const Tb* __restrict__ boxesInput, + const Tb* __restrict__ anchorsInput, const int* __restrict__ topClassData, const int* __restrict__ topAnchorsData, + const int* __restrict__ topNumData, const T* __restrict__ sortedScoresData, const int* __restrict__ sortedIndexData, + T& scoreMap, int& classMap, BoxCorner& boxMap, int& boxIdxMap) +{ + // idx: Holds the NMS box index, within the current batch. + // idxSort: Holds the batched NMS box index, which indexes the (filtered, but sorted) score buffer. + // scoreMap: Holds the score that corresponds to the indexed box being processed by NMS. + if (idx >= topNumData[imageIdx]) + { + return; + } + int idxSort = imageIdx * param.numScoreElements + idx; + scoreMap = sortedScoresData[idxSort]; + + // idxMap: Holds the re-mapped index, which indexes the (filtered, but unsorted) buffers. + // classMap: Holds the class that corresponds to the idx'th sorted score being processed by NMS. + // anchorMap: Holds the anchor that corresponds to the idx'th sorted score being processed by NMS. + int idxMap = imageIdx * param.numScoreElements + sortedIndexData[idxSort]; + classMap = topClassData[idxMap]; + int anchorMap = topAnchorsData[idxMap]; + + // boxIdxMap: Holds the re-re-mapped index, which indexes the (unfiltered, and unsorted) boxes input buffer. + boxIdxMap = -1; + if (param.shareLocation) // Shape of boxesInput: [batchSize, numAnchors, 1, 4] + { + boxIdxMap = imageIdx * param.numAnchors + anchorMap; + } + else // Shape of boxesInput: [batchSize, numAnchors, numClasses, 4] + { + int batchOffset = imageIdx * param.numAnchors * param.numClasses; + int anchorOffset = anchorMap * param.numClasses; + boxIdxMap = batchOffset + anchorOffset + classMap; + } + // anchorIdxMap: Holds the re-re-mapped index, which indexes the (unfiltered, and unsorted) anchors input buffer. + int anchorIdxMap = -1; + if (param.shareAnchors) // Shape of anchorsInput: [1, numAnchors, 4] + { + anchorIdxMap = anchorMap; + } + else // Shape of anchorsInput: [batchSize, numAnchors, 4] + { + anchorIdxMap = imageIdx * param.numAnchors + anchorMap; + } + // boxMap: Holds the box that corresponds to the idx'th sorted score being processed by NMS. + boxMap = DecodeBoxes(param, boxIdxMap, anchorIdxMap, boxesInput, anchorsInput); +} + +template +__device__ void WriteNMSResult(EfficientNMSParameters param, int* __restrict__ numDetectionsOutput, + T* __restrict__ nmsScoresOutput, int* __restrict__ nmsClassesOutput, BoxCorner* __restrict__ nmsBoxesOutput, + T threadScore, int threadClass, BoxCorner threadBox, int imageIdx, unsigned int resultsCounter) +{ + int outputIdx = imageIdx * param.numOutputBoxes + resultsCounter - 1; + if (param.scoreSigmoid) + { + nmsScoresOutput[outputIdx] = sigmoid_mp(threadScore); + } + else if (param.scoreBits > 0) + { + nmsScoresOutput[outputIdx] = add_mp(threadScore, (T) -1); + } + else + { + nmsScoresOutput[outputIdx] = threadScore; + } + nmsClassesOutput[outputIdx] = threadClass; + nmsBoxesOutput[outputIdx] = threadBox; + numDetectionsOutput[imageIdx] = resultsCounter; +} + +__device__ void WriteONNXResult(EfficientNMSParameters param, int* outputIndexData, int* __restrict__ nmsIndicesOutput, + int imageIdx, int threadClass, int boxIdxMap) +{ + int index = boxIdxMap % param.numAnchors; + int idx = atomicAdd((unsigned int*) &outputIndexData[0], 1); + nmsIndicesOutput[idx * 3 + 0] = imageIdx; + nmsIndicesOutput[idx * 3 + 1] = threadClass; + nmsIndicesOutput[idx * 3 + 2] = index; +} + +__global__ void PadONNXResult(EfficientNMSParameters param, int* outputIndexData, int* __restrict__ nmsIndicesOutput) +{ + if (threadIdx.x > 0) + { + return; + } + int pidx = outputIndexData[0] - 1; + if (pidx < 0) + { + return; + } + for (int idx = pidx + 1; idx < param.batchSize * param.numOutputBoxes; idx++) + { + nmsIndicesOutput[idx * 3 + 0] = nmsIndicesOutput[pidx * 3 + 0]; + nmsIndicesOutput[idx * 3 + 1] = nmsIndicesOutput[pidx * 3 + 1]; + nmsIndicesOutput[idx * 3 + 2] = nmsIndicesOutput[pidx * 3 + 2]; + } +} + +template +__global__ void EfficientNMS(EfficientNMSParameters param, const int* topNumData, int* outputIndexData, + int* outputClassData, const int* sortedIndexData, const T* __restrict__ sortedScoresData, + const int* __restrict__ topClassData, const int* __restrict__ topAnchorsData, const Tb* __restrict__ boxesInput, + const Tb* __restrict__ anchorsInput, int* __restrict__ numDetectionsOutput, T* __restrict__ nmsScoresOutput, + int* __restrict__ nmsClassesOutput, int* __restrict__ nmsIndicesOutput, BoxCorner* __restrict__ nmsBoxesOutput) +{ + unsigned int thread = threadIdx.x; + unsigned int imageIdx = blockIdx.y; + unsigned int tileSize = blockDim.x; + if (imageIdx >= param.batchSize) + { + return; + } + + int numSelectedBoxes = min(topNumData[imageIdx], param.numSelectedBoxes); + int numTiles = (numSelectedBoxes + tileSize - 1) / tileSize; + if (thread >= numSelectedBoxes) + { + return; + } + + __shared__ int blockState; + __shared__ unsigned int resultsCounter; + if (thread == 0) + { + blockState = 0; + resultsCounter = 0; + } + + int threadState[4]; + unsigned int boxIdx[4]; + T threadScore[4]; + int threadClass[4]; + BoxCorner threadBox[4]; + int boxIdxMap[4]; + for (int tile = 0; tile < numTiles; tile++) + { + threadState[tile] = 0; + boxIdx[tile] = thread + tile * blockDim.x; + MapNMSData(param, boxIdx[tile], imageIdx, boxesInput, anchorsInput, topClassData, topAnchorsData, + topNumData, sortedScoresData, sortedIndexData, threadScore[tile], threadClass[tile], threadBox[tile], + boxIdxMap[tile]); + } + + // Iterate through all boxes to NMS against. + for (int i = 0; i < numSelectedBoxes; i++) + { + int tile = i / tileSize; + if (boxIdx[tile] == i) + { + // Iteration lead thread, figure out what the other threads should do, + // this will be signaled via the blockState shared variable. + if (threadState[tile] == -1) + { + // Thread already dead, this box was already dropped in a previous iteration, + // because it had a large IOU overlap with another lead thread previously, so + // it would never be kept anyway, therefore it can safely be skip all IOU operations + // in this iteration. + blockState = -1; // -1 => Signal all threads to skip iteration + } + else if (threadState[tile] == 0) + { + // As this box will be kept, this is a good place to find what index in the results buffer it + // should have, as this allows to perform an early loop exit if there are enough results. + if (resultsCounter >= param.numOutputBoxes) + { + blockState = -2; // -2 => Signal all threads to do an early loop exit. + } + else + { + // Thread is still alive, because it has not had a large enough IOU overlap with + // any other kept box previously. Therefore, this box will be kept for sure. However, + // we need to check against all other subsequent boxes from this position onward, + // to see how those other boxes will behave in future iterations. + blockState = 1; // +1 => Signal all (higher index) threads to calculate IOU against this box + threadState[tile] = 1; // +1 => Mark this box's thread to be kept and written out to results + + // If the numOutputBoxesPerClass check is enabled, write the result only if the limit for this + // class on this image has not been reached yet. Other than (possibly) skipping the write, this + // won't affect anything else in the NMS threading. + bool write = true; + if (param.numOutputBoxesPerClass >= 0) + { + int classCounterIdx = imageIdx * param.numClasses + threadClass[tile]; + write = (outputClassData[classCounterIdx] < param.numOutputBoxesPerClass); + outputClassData[classCounterIdx]++; + } + if (write) + { + // This branch is visited by one thread per iteration, so it's safe to do non-atomic increments. + resultsCounter++; + if (param.outputONNXIndices) + { + WriteONNXResult( + param, outputIndexData, nmsIndicesOutput, imageIdx, threadClass[tile], boxIdxMap[tile]); + } + else + { + WriteNMSResult(param, numDetectionsOutput, nmsScoresOutput, nmsClassesOutput, + nmsBoxesOutput, threadScore[tile], threadClass[tile], threadBox[tile], imageIdx, + resultsCounter); + } + } + } + } + else + { + // This state should never be reached, but just in case... + blockState = 0; // 0 => Signal all threads to not do any updates, nothing happens. + } + } + + __syncthreads(); + + if (blockState == -2) + { + // This is the signal to exit from the loop. + return; + } + + if (blockState == -1) + { + // This is the signal for all threads to just skip this iteration, as no IOU's need to be checked. + continue; + } + + // Grab a box and class to test the current box against. The test box corresponds to iteration i, + // therefore it will have a lower index than the current thread box, and will therefore have a higher score + // than the current box because it's located "before" in the sorted score list. + T testScore; + int testClass; + BoxCorner testBox; + int testBoxIdxMap; + MapNMSData(param, i, imageIdx, boxesInput, anchorsInput, topClassData, topAnchorsData, topNumData, + sortedScoresData, sortedIndexData, testScore, testClass, testBox, testBoxIdxMap); + + for (int tile = 0; tile < numTiles; tile++) + { + // IOU + if (boxIdx[tile] > i && // Make sure two different boxes are being tested, and that it's a higher index; + boxIdx[tile] < numSelectedBoxes && // Make sure the box is within numSelectedBoxes; + blockState == 1 && // Signal that allows IOU checks to be performed; + threadState[tile] == 0 && // Make sure this box hasn't been either dropped or kept already; + threadClass[tile] == testClass && // Compare only boxes of matching classes; + lte_mp(threadScore[tile], testScore) && // Make sure the sorting order of scores is as expected; + IOU(param, threadBox[tile], testBox) >= param.iouThreshold) // And... IOU overlap. + { + // Current box overlaps with the box tested in this iteration, this box will be skipped. + threadState[tile] = -1; // -1 => Mark this box's thread to be dropped. + } + } + } +} + +template +cudaError_t EfficientNMSLauncher(EfficientNMSParameters& param, int* topNumData, int* outputIndexData, + int* outputClassData, int* sortedIndexData, T* sortedScoresData, int* topClassData, int* topAnchorsData, + const void* boxesInput, const void* anchorsInput, int* numDetectionsOutput, T* nmsScoresOutput, + int* nmsClassesOutput, int* nmsIndicesOutput, void* nmsBoxesOutput, cudaStream_t stream) +{ + unsigned int tileSize = 1024; + if (param.numSelectedBoxes <= 512) + { + tileSize = 512; + } + if (param.numSelectedBoxes <= 256) + { + tileSize = 256; + } + + const dim3 blockSize = {tileSize, 1, 1}; + const dim3 gridSize = {1, (unsigned int) param.batchSize, 1}; + + if (param.boxCoding == 0) + { + EfficientNMS><<>>(param, topNumData, outputIndexData, + outputClassData, sortedIndexData, sortedScoresData, topClassData, topAnchorsData, + (BoxCorner*) boxesInput, (BoxCorner*) anchorsInput, numDetectionsOutput, nmsScoresOutput, + nmsClassesOutput, nmsIndicesOutput, (BoxCorner*) nmsBoxesOutput); + } + else if (param.boxCoding == 1) + { + // Note that nmsBoxesOutput is always coded as BoxCorner, regardless of the input coding type. + EfficientNMS><<>>(param, topNumData, outputIndexData, + outputClassData, sortedIndexData, sortedScoresData, topClassData, topAnchorsData, + (BoxCenterSize*) boxesInput, (BoxCenterSize*) anchorsInput, numDetectionsOutput, nmsScoresOutput, + nmsClassesOutput, nmsIndicesOutput, (BoxCorner*) nmsBoxesOutput); + } + + if (param.outputONNXIndices) + { + PadONNXResult<<<{1}, {1}, 0, stream>>>(param, outputIndexData, nmsIndicesOutput); + } + + return cudaGetLastError(); +} + +__global__ void EfficientNMSFilterSegments(EfficientNMSParameters param, const int* __restrict__ topNumData, + int* __restrict__ topOffsetsStartData, int* __restrict__ topOffsetsEndData) +{ + int imageIdx = threadIdx.x; + if (imageIdx > param.batchSize) + { + return; + } + topOffsetsStartData[imageIdx] = imageIdx * param.numScoreElements; + topOffsetsEndData[imageIdx] = imageIdx * param.numScoreElements + topNumData[imageIdx]; +} + +template +__global__ void EfficientNMSFilter(EfficientNMSParameters param, const T* __restrict__ scoresInput, + int* __restrict__ topNumData, int* __restrict__ topIndexData, int* __restrict__ topAnchorsData, + T* __restrict__ topScoresData, int* __restrict__ topClassData) +{ + int elementIdx = blockDim.x * blockIdx.x + threadIdx.x; + int imageIdx = blockDim.y * blockIdx.y + threadIdx.y; + + // Boundary Conditions + if (elementIdx >= param.numScoreElements || imageIdx >= param.batchSize) + { + return; + } + + // Shape of scoresInput: [batchSize, numAnchors, numClasses] + int scoresInputIdx = imageIdx * param.numScoreElements + elementIdx; + + // For each class, check its corresponding score if it crosses the threshold, and if so select this anchor, + // and keep track of the maximum score and the corresponding (argmax) class id + T score = scoresInput[scoresInputIdx]; + if (gte_mp(score, (T) param.scoreThreshold)) + { + // Unpack the class and anchor index from the element index + int classIdx = elementIdx % param.numClasses; + int anchorIdx = elementIdx / param.numClasses; + + // If this is a background class, ignore it. + if (classIdx == param.backgroundClass) + { + return; + } + + // Use an atomic to find an open slot where to write the selected anchor data. + if (topNumData[imageIdx] >= param.numScoreElements) + { + return; + } + int selectedIdx = atomicAdd((unsigned int*) &topNumData[imageIdx], 1); + if (selectedIdx >= param.numScoreElements) + { + topNumData[imageIdx] = param.numScoreElements; + return; + } + + // Shape of topScoresData / topClassData: [batchSize, numScoreElements] + int topIdx = imageIdx * param.numScoreElements + selectedIdx; + + if (param.scoreBits > 0) + { + add_mp(score, (T) 1); + if (gt_mp(score, (T) (2.f - 1.f / 1024.f))) + { + // Ensure the incremented score fits in the mantissa without changing the exponent + score = (2.f - 1.f / 1024.f); + } + } + + topIndexData[topIdx] = selectedIdx; + topAnchorsData[topIdx] = anchorIdx; + topScoresData[topIdx] = score; + topClassData[topIdx] = classIdx; + } +} + +template +__global__ void EfficientNMSDenseIndex(EfficientNMSParameters param, int* __restrict__ topNumData, + int* __restrict__ topIndexData, int* __restrict__ topAnchorsData, int* __restrict__ topOffsetsStartData, + int* __restrict__ topOffsetsEndData, T* __restrict__ topScoresData, int* __restrict__ topClassData) +{ + int elementIdx = blockDim.x * blockIdx.x + threadIdx.x; + int imageIdx = blockDim.y * blockIdx.y + threadIdx.y; + + if (elementIdx >= param.numScoreElements || imageIdx >= param.batchSize) + { + return; + } + + int dataIdx = imageIdx * param.numScoreElements + elementIdx; + int anchorIdx = elementIdx / param.numClasses; + int classIdx = elementIdx % param.numClasses; + if (param.scoreBits > 0) + { + T score = topScoresData[dataIdx]; + if (lt_mp(score, (T) param.scoreThreshold)) + { + score = (T) 1; + } + else if (classIdx == param.backgroundClass) + { + score = (T) 1; + } + else + { + score = add_mp(score, (T) 1); + if (gt_mp(score, (T) (2.f - 1.f / 1024.f))) + { + // Ensure the incremented score fits in the mantissa without changing the exponent + score = (2.f - 1.f / 1024.f); + } + } + topScoresData[dataIdx] = score; + } + else + { + T score = topScoresData[dataIdx]; + if (lt_mp(score, (T) param.scoreThreshold)) + { + topScoresData[dataIdx] = -1 << 15; + } + else if (classIdx == param.backgroundClass) + { + topScoresData[dataIdx] = -1 << 15; + } + } + + topIndexData[dataIdx] = elementIdx; + topAnchorsData[dataIdx] = anchorIdx; + topClassData[dataIdx] = classIdx; + + if (elementIdx == 0) + { + // Saturate counters + topNumData[imageIdx] = param.numScoreElements; + topOffsetsStartData[imageIdx] = imageIdx * param.numScoreElements; + topOffsetsEndData[imageIdx] = (imageIdx + 1) * param.numScoreElements; + } +} + +template +cudaError_t EfficientNMSFilterLauncher(EfficientNMSParameters& param, const T* scoresInput, int* topNumData, + int* topIndexData, int* topAnchorsData, int* topOffsetsStartData, int* topOffsetsEndData, T* topScoresData, + int* topClassData, cudaStream_t stream) +{ + const unsigned int elementsPerBlock = 512; + const unsigned int imagesPerBlock = 1; + const unsigned int elementBlocks = (param.numScoreElements + elementsPerBlock - 1) / elementsPerBlock; + const unsigned int imageBlocks = (param.batchSize + imagesPerBlock - 1) / imagesPerBlock; + const dim3 blockSize = {elementsPerBlock, imagesPerBlock, 1}; + const dim3 gridSize = {elementBlocks, imageBlocks, 1}; + + float kernelSelectThreshold = 0.007f; + if (param.scoreSigmoid) + { + // Inverse Sigmoid + if (param.scoreThreshold <= 0.f) + { + param.scoreThreshold = -1 << 15; + } + else + { + param.scoreThreshold = logf(param.scoreThreshold / (1.f - param.scoreThreshold)); + } + kernelSelectThreshold = logf(kernelSelectThreshold / (1.f - kernelSelectThreshold)); + // Disable Score Bits Optimization + param.scoreBits = -1; + } + + if (param.scoreThreshold < kernelSelectThreshold) + { + // A full copy of the buffer is necessary because sorting will scramble the input data otherwise. + cudaMemcpyAsync(topScoresData, scoresInput, param.batchSize * param.numScoreElements * sizeof(T), + cudaMemcpyDeviceToDevice, stream); + + EfficientNMSDenseIndex<<>>(param, topNumData, topIndexData, topAnchorsData, + topOffsetsStartData, topOffsetsEndData, topScoresData, topClassData); + } + else + { + EfficientNMSFilter<<>>( + param, scoresInput, topNumData, topIndexData, topAnchorsData, topScoresData, topClassData); + + EfficientNMSFilterSegments<<<1, param.batchSize, 0, stream>>>( + param, topNumData, topOffsetsStartData, topOffsetsEndData); + } + + return cudaGetLastError(); +} + +template +size_t EfficientNMSSortWorkspaceSize(EfficientNMSParameters param) +{ + size_t sortedWorkspaceSize = 0; + cub::DoubleBuffer keysDB(nullptr, nullptr); + cub::DoubleBuffer valuesDB(nullptr, nullptr); + cub::DeviceSegmentedRadixSort::SortPairsDescending(nullptr, sortedWorkspaceSize, keysDB, valuesDB, + param.numScoreElements, param.batchSize, (const int*) nullptr, (const int*) nullptr); + return sortedWorkspaceSize; +} + +size_t EfficientNMSWorkspaceSize(EfficientNMSParameters param) +{ + size_t total = 0, size = 0, align = 256; + // Counters + // 3 for Filtering + // 1 for Output Indexing + // C for Max per Class Limiting + size = (3 + 1 + param.numClasses) * param.batchSize * sizeof(int); + total += size + (size % align ? align - (size % align) : 0); + // Int Buffers + for (int i = 0; i < 4; i++) + { + size = param.batchSize * param.numScoreElements * sizeof(int); + total += size + (size % align ? align - (size % align) : 0); + } + // Float Buffers + for (int i = 0; i < 2; i++) + { + size = param.batchSize * param.numScoreElements * dataTypeSize(param.datatype); + total += size + (size % align ? align - (size % align) : 0); + } + // Sort Workspace + if (param.datatype == DataType::kHALF) + { + size = EfficientNMSSortWorkspaceSize<__half>(param); + total += size + (size % align ? align - (size % align) : 0); + } + else if (param.datatype == DataType::kFLOAT) + { + size = EfficientNMSSortWorkspaceSize(param); + total += size + (size % align ? align - (size % align) : 0); + } + + return total; +} + +template +T* EfficientNMSWorkspace(void* workspace, size_t& offset, size_t elements) +{ + T* buffer = (T*) ((size_t) workspace + offset); + size_t align = 256; + size_t size = elements * sizeof(T); + size_t sizeAligned = size + (size % align ? align - (size % align) : 0); + offset += sizeAligned; + return buffer; +} + +template +pluginStatus_t EfficientNMSDispatch(EfficientNMSParameters param, const void* boxesInput, const void* scoresInput, + const void* anchorsInput, void* numDetectionsOutput, void* nmsBoxesOutput, void* nmsScoresOutput, + void* nmsClassesOutput, void* nmsIndicesOutput, void* workspace, cudaStream_t stream) +{ + // Clear Outputs (not all elements will get overwritten by the kernels, so safer to clear everything out) + if (param.outputONNXIndices) + { + cudaMemsetAsync(nmsIndicesOutput, 0xFF, param.batchSize * param.numOutputBoxes * 3 * sizeof(int), stream); + } + else + { + cudaMemsetAsync(numDetectionsOutput, 0x00, param.batchSize * sizeof(int), stream); + cudaMemsetAsync(nmsScoresOutput, 0x00, param.batchSize * param.numOutputBoxes * sizeof(T), stream); + cudaMemsetAsync(nmsBoxesOutput, 0x00, param.batchSize * param.numOutputBoxes * 4 * sizeof(T), stream); + cudaMemsetAsync(nmsClassesOutput, 0x00, param.batchSize * param.numOutputBoxes * sizeof(int), stream); + } + + // Counters Workspace + size_t workspaceOffset = 0; + int countersTotalSize = (3 + 1 + param.numClasses) * param.batchSize; + int* topNumData = EfficientNMSWorkspace(workspace, workspaceOffset, countersTotalSize); + int* topOffsetsStartData = topNumData + param.batchSize; + int* topOffsetsEndData = topNumData + 2 * param.batchSize; + int* outputIndexData = topNumData + 3 * param.batchSize; + int* outputClassData = topNumData + 4 * param.batchSize; + cudaMemsetAsync(topNumData, 0x00, countersTotalSize * sizeof(int), stream); + cudaError_t status = cudaGetLastError(); + CSC(status, STATUS_FAILURE); + + // Other Buffers Workspace + int* topIndexData + = EfficientNMSWorkspace(workspace, workspaceOffset, param.batchSize * param.numScoreElements); + int* topClassData + = EfficientNMSWorkspace(workspace, workspaceOffset, param.batchSize * param.numScoreElements); + int* topAnchorsData + = EfficientNMSWorkspace(workspace, workspaceOffset, param.batchSize * param.numScoreElements); + int* sortedIndexData + = EfficientNMSWorkspace(workspace, workspaceOffset, param.batchSize * param.numScoreElements); + T* topScoresData = EfficientNMSWorkspace(workspace, workspaceOffset, param.batchSize * param.numScoreElements); + T* sortedScoresData + = EfficientNMSWorkspace(workspace, workspaceOffset, param.batchSize * param.numScoreElements); + size_t sortedWorkspaceSize = EfficientNMSSortWorkspaceSize(param); + char* sortedWorkspaceData = EfficientNMSWorkspace(workspace, workspaceOffset, sortedWorkspaceSize); + cub::DoubleBuffer scoresDB(topScoresData, sortedScoresData); + cub::DoubleBuffer indexDB(topIndexData, sortedIndexData); + + // Kernels + status = EfficientNMSFilterLauncher(param, (T*) scoresInput, topNumData, topIndexData, topAnchorsData, + topOffsetsStartData, topOffsetsEndData, topScoresData, topClassData, stream); + CSC(status, STATUS_FAILURE); + + status = cub::DeviceSegmentedRadixSort::SortPairsDescending(sortedWorkspaceData, sortedWorkspaceSize, scoresDB, + indexDB, param.batchSize * param.numScoreElements, param.batchSize, topOffsetsStartData, topOffsetsEndData, + param.scoreBits > 0 ? (10 - param.scoreBits) : 0, param.scoreBits > 0 ? 10 : sizeof(T) * 8, stream, false); + CSC(status, STATUS_FAILURE); + + status = EfficientNMSLauncher(param, topNumData, outputIndexData, outputClassData, indexDB.Current(), + scoresDB.Current(), topClassData, topAnchorsData, boxesInput, anchorsInput, (int*) numDetectionsOutput, + (T*) nmsScoresOutput, (int*) nmsClassesOutput, (int*) nmsIndicesOutput, nmsBoxesOutput, stream); + CSC(status, STATUS_FAILURE); + + return STATUS_SUCCESS; +} + +pluginStatus_t EfficientNMSInference(EfficientNMSParameters param, const void* boxesInput, const void* scoresInput, + const void* anchorsInput, void* numDetectionsOutput, void* nmsBoxesOutput, void* nmsScoresOutput, + void* nmsClassesOutput, void* nmsIndicesOutput, void* workspace, cudaStream_t stream) +{ + if (param.datatype == DataType::kFLOAT) + { + param.scoreBits = -1; + return EfficientNMSDispatch(param, boxesInput, scoresInput, anchorsInput, numDetectionsOutput, + nmsBoxesOutput, nmsScoresOutput, nmsClassesOutput, nmsIndicesOutput, workspace, stream); + } + else if (param.datatype == DataType::kHALF) + { + if (param.scoreBits <= 0 || param.scoreBits > 10) + { + param.scoreBits = -1; + } + return EfficientNMSDispatch<__half>(param, boxesInput, scoresInput, anchorsInput, numDetectionsOutput, + nmsBoxesOutput, nmsScoresOutput, nmsClassesOutput, nmsIndicesOutput, workspace, stream); + } + else + { + return STATUS_NOT_SUPPORTED; + } +} diff --git a/plugin/efficientNMSPlugin/efficientNMSInference.cuh b/plugin/efficientNMSPlugin/efficientNMSInference.cuh new file mode 100644 index 00000000..e3af9360 --- /dev/null +++ b/plugin/efficientNMSPlugin/efficientNMSInference.cuh @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ + +#ifndef TRT_EFFICIENT_NMS_INFERENCE_CUH +#define TRT_EFFICIENT_NMS_INFERENCE_CUH + +#include + +// FP32 Intrinsics + +float __device__ __inline__ exp_mp(const float a) +{ + return __expf(a); +} +float __device__ __inline__ sigmoid_mp(const float a) +{ + return __frcp_rn(__fadd_rn(1.f, __expf(-a))); +} +float __device__ __inline__ add_mp(const float a, const float b) +{ + return __fadd_rn(a, b); +} +float __device__ __inline__ sub_mp(const float a, const float b) +{ + return __fsub_rn(a, b); +} +float __device__ __inline__ mul_mp(const float a, const float b) +{ + return __fmul_rn(a, b); +} +bool __device__ __inline__ gt_mp(const float a, const float b) +{ + return a > b; +} +bool __device__ __inline__ lt_mp(const float a, const float b) +{ + return a < b; +} +bool __device__ __inline__ lte_mp(const float a, const float b) +{ + return a <= b; +} +bool __device__ __inline__ gte_mp(const float a, const float b) +{ + return a >= b; +} + +#if __CUDA_ARCH__ >= 530 + +// FP16 Intrinsics + +__half __device__ __inline__ exp_mp(const __half a) +{ + return hexp(a); +} +__half __device__ __inline__ sigmoid_mp(const __half a) +{ + return hrcp(__hadd((__half) 1, hexp(__hneg(a)))); +} +__half __device__ __inline__ add_mp(const __half a, const __half b) +{ + return __hadd(a, b); +} +__half __device__ __inline__ sub_mp(const __half a, const __half b) +{ + return __hsub(a, b); +} +__half __device__ __inline__ mul_mp(const __half a, const __half b) +{ + return __hmul(a, b); +} +bool __device__ __inline__ gt_mp(const __half a, const __half b) +{ + return __hgt(a, b); +} +bool __device__ __inline__ lt_mp(const __half a, const __half b) +{ + return __hlt(a, b); +} +bool __device__ __inline__ lte_mp(const __half a, const __half b) +{ + return __hle(a, b); +} +bool __device__ __inline__ gte_mp(const __half a, const __half b) +{ + return __hge(a, b); +} + +#else + +// FP16 Fallbacks on older architectures that lack support + +__half __device__ __inline__ exp_mp(const __half a) +{ + return __float2half(exp_mp(__half2float(a))); +} +__half __device__ __inline__ sigmoid_mp(const __half a) +{ + return __float2half(sigmoid_mp(__half2float(a))); +} +__half __device__ __inline__ add_mp(const __half a, const __half b) +{ + return __float2half(add_mp(__half2float(a), __half2float(b))); +} +__half __device__ __inline__ sub_mp(const __half a, const __half b) +{ + return __float2half(sub_mp(__half2float(a), __half2float(b))); +} +__half __device__ __inline__ mul_mp(const __half a, const __half b) +{ + return __float2half(mul_mp(__half2float(a), __half2float(b))); +} +bool __device__ __inline__ gt_mp(const __half a, const __half b) +{ + return __float2half(gt_mp(__half2float(a), __half2float(b))); +} +bool __device__ __inline__ lt_mp(const __half a, const __half b) +{ + return __float2half(lt_mp(__half2float(a), __half2float(b))); +} +bool __device__ __inline__ lte_mp(const __half a, const __half b) +{ + return __float2half(lte_mp(__half2float(a), __half2float(b))); +} +bool __device__ __inline__ gte_mp(const __half a, const __half b) +{ + return __float2half(gte_mp(__half2float(a), __half2float(b))); +} + +#endif + +template +struct __align__(4 * sizeof(T)) BoxCorner; + +template +struct __align__(4 * sizeof(T)) BoxCenterSize; + +template +struct __align__(4 * sizeof(T)) BoxCorner +{ + // For NMS/IOU purposes, YXYX coding is identical to XYXY + T y1, x1, y2, x2; + + __device__ void reorder() + { + if (gt_mp(y1, y2)) + { + // Swap values, so y1 < y2 + y1 = sub_mp(y1, y2); + y2 = add_mp(y1, y2); + y1 = sub_mp(y2, y1); + } + if (gt_mp(x1, x2)) + { + // Swap values, so x1 < x2 + x1 = sub_mp(x1, x2); + x2 = add_mp(x1, x2); + x1 = sub_mp(x2, x1); + } + } + + __device__ BoxCorner clip(T low, T high) const + { + return {lt_mp(y1, low) ? low : (gt_mp(y1, high) ? high : y1), + lt_mp(x1, low) ? low : (gt_mp(x1, high) ? high : x1), lt_mp(y2, low) ? low : (gt_mp(y2, high) ? high : y2), + lt_mp(x2, low) ? low : (gt_mp(x2, high) ? high : x2)}; + } + + __device__ BoxCorner decode(BoxCorner anchor) const + { + return {add_mp(y1, anchor.y1), add_mp(x1, anchor.x1), add_mp(y2, anchor.y2), add_mp(x2, anchor.x2)}; + } + + __device__ float area() const + { + T w = sub_mp(x2, x1); + T h = sub_mp(y2, y1); + if (lte_mp(h, (T) 0)) + { + return 0; + } + if (lte_mp(w, (T) 0)) + { + return 0; + } + return (float) h * (float) w; + } + + __device__ operator BoxCenterSize() const + { + T w = x2 - x1; + T h = y2 - y1; + return BoxCenterSize{y1 + (T) 0.5 * h, x1 + (T) 0.5 * w, h, w}; + } + + __device__ static BoxCorner intersect(BoxCorner a, BoxCorner b) + { + return {gt_mp(a.y1, b.y1) ? a.y1 : b.y1, gt_mp(a.x1, b.x1) ? a.x1 : b.x1, lt_mp(a.y2, b.y2) ? a.y2 : b.y2, + lt_mp(a.x2, b.x2) ? a.x2 : b.x2}; + } +}; + +template +struct __align__(4 * sizeof(T)) BoxCenterSize +{ + // For NMS/IOU purposes, YXHW coding is identical to XYWH + T y, x, h, w; + + __device__ void reorder() {} + + __device__ BoxCenterSize clip(T low, T high) const + { + return BoxCenterSize(BoxCorner(*this).clip(low, high)); + } + + __device__ BoxCenterSize decode(BoxCenterSize anchor) const + { + return {add_mp(mul_mp(y, anchor.h), anchor.y), add_mp(mul_mp(x, anchor.w), anchor.x), + mul_mp(anchor.h, exp_mp(h)), mul_mp(anchor.w, exp_mp(w))}; + } + + __device__ float area() const + { + if (h <= (T) 0) + { + return 0; + } + if (w <= (T) 0) + { + return 0; + } + return (float) h * (float) w; + } + + __device__ operator BoxCorner() const + { + T h2 = mul_mp(h, (T) 0.5); + T w2 = mul_mp(w, (T) 0.5); + return BoxCorner{sub_mp(y, h2), sub_mp(x, w2), add_mp(y, h2), add_mp(x, w2)}; + } + __device__ static BoxCenterSize intersect(BoxCenterSize a, BoxCenterSize b) + { + return BoxCenterSize(BoxCorner::intersect(BoxCorner(a), BoxCorner(b))); + } +}; + +#endif \ No newline at end of file diff --git a/plugin/efficientNMSPlugin/efficientNMSInference.h b/plugin/efficientNMSPlugin/efficientNMSInference.h new file mode 100644 index 00000000..de375df8 --- /dev/null +++ b/plugin/efficientNMSPlugin/efficientNMSInference.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ + +#ifndef TRT_EFFICIENT_NMS_INFERENCE_H +#define TRT_EFFICIENT_NMS_INFERENCE_H + +#include "plugin.h" + +#include "efficientNMSParameters.h" + +size_t EfficientNMSWorkspaceSize(EfficientNMSParameters param); + +pluginStatus_t EfficientNMSInference(EfficientNMSParameters param, const void* boxesInput, const void* scoresInput, + const void* anchorsInput, void* numDetectionsOutput, void* nmsBoxesOutput, void* nmsScoresOutput, + void* nmsClassesOutput, void* nmsIndicesOutput, void* workspace, cudaStream_t stream); + +#endif diff --git a/plugin/efficientNMSPlugin/efficientNMSParameters.h b/plugin/efficientNMSPlugin/efficientNMSParameters.h new file mode 100644 index 00000000..a1632aa6 --- /dev/null +++ b/plugin/efficientNMSPlugin/efficientNMSParameters.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ + +#ifndef TRT_EFFICIENT_NMS_PARAMETERS_H +#define TRT_EFFICIENT_NMS_PARAMETERS_H + +#include "plugin.h" + +using namespace nvinfer1::plugin; +namespace nvinfer1 +{ +namespace plugin +{ + +struct EfficientNMSParameters +{ + // Related to NMS Options + float iouThreshold = 0.5f; + float scoreThreshold = 0.5f; + int numOutputBoxes = 100; + int numOutputBoxesPerClass = -1; + int backgroundClass = -1; + bool scoreSigmoid = false; + int boxCoding = 0; + + // Related to NMS Internals + int numSelectedBoxes = 4096; + int scoreBits = 10; + bool outputONNXIndices = false; + + // Related to Tensor Configuration + // (These are set by the various plugin configuration methods, no need to define them during plugin creation.) + int batchSize = -1; + int numClasses = 1; + int numBoxElements = -1; + int numScoreElements = -1; + int numAnchors = -1; + bool shareLocation = true; + bool shareAnchors = true; + bool boxDecoder = false; + nvinfer1::DataType datatype = nvinfer1::DataType::kFLOAT; +}; + +} // namespace plugin +} // namespace nvinfer1 + +#endif diff --git a/plugin/efficientNMSPlugin/efficientNMSPlugin.cpp b/plugin/efficientNMSPlugin/efficientNMSPlugin.cpp new file mode 100644 index 00000000..1ee1bc41 --- /dev/null +++ b/plugin/efficientNMSPlugin/efficientNMSPlugin.cpp @@ -0,0 +1,565 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * + * 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 "efficientNMSPlugin.h" +#include "efficientNMSInference.h" + +using namespace nvinfer1; +using nvinfer1::plugin::EfficientNMSPlugin; +using nvinfer1::plugin::EfficientNMSParameters; +using nvinfer1::plugin::EfficientNMSPluginCreator; +using nvinfer1::plugin::EfficientNMSONNXPluginCreator; + +namespace +{ +const char* EFFICIENT_NMS_PLUGIN_VERSION{"1"}; +const char* EFFICIENT_NMS_PLUGIN_NAME{"EfficientNMS_TRT"}; +const char* EFFICIENT_NMS_ONNX_PLUGIN_VERSION{"1"}; +const char* EFFICIENT_NMS_ONNX_PLUGIN_NAME{"EfficientNMS_ONNX_TRT"}; +} // namespace + +PluginFieldCollection EfficientNMSPluginCreator::mFC{}; +PluginFieldCollection EfficientNMSONNXPluginCreator::mFC{}; +std::vector EfficientNMSPluginCreator::mPluginAttributes; +std::vector EfficientNMSONNXPluginCreator::mPluginAttributes; + +EfficientNMSPlugin::EfficientNMSPlugin(EfficientNMSParameters param) + : mParam(param) +{ +} + +EfficientNMSPlugin::EfficientNMSPlugin(const void* data, size_t length) +{ + const char *d = reinterpret_cast(data), *a = d; + mParam = read(d); + ASSERT(d == a + length); +} + +const char* EfficientNMSPlugin::getPluginType() const noexcept +{ + return EFFICIENT_NMS_PLUGIN_NAME; +} + +const char* EfficientNMSPlugin::getPluginVersion() const noexcept +{ + return EFFICIENT_NMS_PLUGIN_VERSION; +} + +int EfficientNMSPlugin::getNbOutputs() const noexcept +{ + if (mParam.outputONNXIndices) + { + // ONNX NonMaxSuppression Compatibility + return 1; + } + else + { + // Standard Plugin Implementation + return 4; + } +} + +int EfficientNMSPlugin::initialize() noexcept +{ + return STATUS_SUCCESS; +} + +void EfficientNMSPlugin::terminate() noexcept {} + +size_t EfficientNMSPlugin::getSerializationSize() const noexcept +{ + return sizeof(EfficientNMSParameters); +} + +void EfficientNMSPlugin::serialize(void* buffer) const noexcept +{ + char *d = reinterpret_cast(buffer), *a = d; + write(d, mParam); + ASSERT(d == a + getSerializationSize()); +} + +void EfficientNMSPlugin::destroy() noexcept +{ + delete this; +} + +void EfficientNMSPlugin::setPluginNamespace(const char* pluginNamespace) noexcept +{ + try + { + mNamespace = pluginNamespace; + } + catch (const std::exception& e) + { + caughtError(e); + } +} + +const char* EfficientNMSPlugin::getPluginNamespace() const noexcept +{ + return mNamespace.c_str(); +} + +nvinfer1::DataType EfficientNMSPlugin::getOutputDataType( + int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept +{ + if (mParam.outputONNXIndices) + { + // ONNX NMS uses an integer output + return nvinfer1::DataType::kINT32; + } + else + { + // On standard NMS, num_detections and detection_classes use integer outputs + if (index == 0 || index == 3) + { + return nvinfer1::DataType::kINT32; + } + // All others should use the same datatype as the input + return inputTypes[0]; + } +} + +IPluginV2DynamicExt* EfficientNMSPlugin::clone() const noexcept +{ + try + { + auto* plugin = new EfficientNMSPlugin(mParam); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; +} + +DimsExprs EfficientNMSPlugin::getOutputDimensions( + int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) noexcept +{ + try + { + DimsExprs out_dim; + + if (mParam.outputONNXIndices) + { + // ONNX NMS + ASSERT(outputIndex == 0); + + // detection_indices + out_dim.nbDims = 2; + out_dim.d[0] = exprBuilder.operation( + DimensionOperation::kPROD, *inputs[0].d[0], *exprBuilder.constant(mParam.numOutputBoxes)); + out_dim.d[1] = exprBuilder.constant(3); + } + else + { + // Standard NMS + ASSERT(outputIndex >= 0 && outputIndex <= 3); + + // num_detections + if (outputIndex == 0) + { + out_dim.nbDims = 2; + out_dim.d[0] = inputs[0].d[0]; + out_dim.d[1] = exprBuilder.constant(1); + } + // detection_boxes + else if (outputIndex == 1) + { + out_dim.nbDims = 3; + out_dim.d[0] = inputs[0].d[0]; + out_dim.d[1] = exprBuilder.constant(mParam.numOutputBoxes); + out_dim.d[2] = exprBuilder.constant(4); + } + // detection_scores + else if (outputIndex == 2) + { + out_dim.nbDims = 2; + out_dim.d[0] = inputs[0].d[0]; + out_dim.d[1] = exprBuilder.constant(mParam.numOutputBoxes); + } + // detection_classes + else if (outputIndex == 3) + { + out_dim.nbDims = 2; + out_dim.d[0] = inputs[0].d[0]; + out_dim.d[1] = exprBuilder.constant(mParam.numOutputBoxes); + } + } + + return out_dim; + } + catch (const std::exception& e) + { + caughtError(e); + } + return DimsExprs{}; +} + +bool EfficientNMSPlugin::supportsFormatCombination( + int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept +{ + if (inOut[pos].format != PluginFormat::kLINEAR) + { + return false; + } + + if (mParam.outputONNXIndices) + { + ASSERT(nbInputs == 2); + ASSERT(nbOutputs == 1); + + // detection_indices output: int + if (pos == 2) + { + return inOut[pos].type == DataType::kINT32; + } + + // boxes and scores input: fp32 or fp16 + return (inOut[pos].type == DataType::kHALF || inOut[pos].type == DataType::kFLOAT) + && (inOut[0].type == inOut[pos].type); + } + else + { + ASSERT(nbInputs == 2 || nbInputs == 3); + ASSERT(nbOutputs == 4); + if (nbInputs == 2) + { + ASSERT(0 <= pos && pos <= 5); + } + if (nbInputs == 3) + { + ASSERT(0 <= pos && pos <= 6); + } + + // num_detections and detection_classes output: int + const int posOut = pos - nbInputs; + if (posOut == 0 || posOut == 3) + { + return inOut[pos].type == DataType::kINT32 && inOut[pos].format == PluginFormat::kLINEAR; + } + + // all other inputs/outputs: fp32 or fp16 + return (inOut[pos].type == DataType::kHALF || inOut[pos].type == DataType::kFLOAT) + && (inOut[0].type == inOut[pos].type); + } +} + +void EfficientNMSPlugin::configurePlugin( + const DynamicPluginTensorDesc* in, int nbInputs, const DynamicPluginTensorDesc* out, int nbOutputs) noexcept +{ + try + { + if (mParam.outputONNXIndices) + { + // Accepts two inputs + // [0] boxes, [1] scores + ASSERT(nbInputs == 2); + ASSERT(nbOutputs == 1); + } + else + { + // Accepts two or three inputs + // If two inputs: [0] boxes, [1] scores + // If three inputs: [0] boxes, [1] scores, [2] anchors + ASSERT(nbInputs == 2 || nbInputs == 3); + ASSERT(nbOutputs == 4); + } + mParam.datatype = in[0].desc.type; + + // Shape of scores input should be + // [batch_size, num_boxes, num_classes] or [batch_size, num_boxes, num_classes, 1] + ASSERT(in[1].desc.dims.nbDims == 3 || (in[1].desc.dims.nbDims == 4 && in[1].desc.dims.d[3] == 1)); + mParam.numScoreElements = in[1].desc.dims.d[1] * in[1].desc.dims.d[2]; + mParam.numClasses = in[1].desc.dims.d[2]; + + // Shape of boxes input should be + // [batch_size, num_boxes, 4] or [batch_size, num_boxes, 1, 4] or [batch_size, num_boxes, num_classes, 4] + ASSERT(in[0].desc.dims.nbDims == 3 || in[0].desc.dims.nbDims == 4); + if (in[0].desc.dims.nbDims == 3) + { + ASSERT(in[0].desc.dims.d[2] == 4); + mParam.shareLocation = true; + mParam.numBoxElements = in[0].desc.dims.d[1] * in[0].desc.dims.d[2]; + } + else + { + ASSERT(in[0].desc.dims.d[2] == mParam.numClasses); + ASSERT(in[0].desc.dims.d[3] == 4); + mParam.shareLocation = (in[0].desc.dims.d[2] == 1); + mParam.numBoxElements = in[0].desc.dims.d[1] * in[0].desc.dims.d[2] * in[0].desc.dims.d[3]; + } + mParam.numAnchors = in[0].desc.dims.d[1]; + + if (nbInputs == 2) + { + // Only two inputs are used, disable the fused box decoder + mParam.boxDecoder = false; + } + if (nbInputs == 3) + { + // All three inputs are used, enable the box decoder + // Shape of anchors input should be + // Constant shape: [1, numAnchors, 4] or [batch_size, numAnchors, 4] + ASSERT(in[2].desc.dims.nbDims == 3); + mParam.boxDecoder = true; + mParam.shareAnchors = (in[2].desc.dims.d[0] == 1); + } + } + catch (const std::exception& e) + { + caughtError(e); + } +} + +size_t EfficientNMSPlugin::getWorkspaceSize( + const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const noexcept +{ + EfficientNMSParameters p = mParam; + p.batchSize = inputs[0].dims.d[0]; + return EfficientNMSWorkspaceSize(p); +} + +int EfficientNMSPlugin::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept +{ + try + { + mParam.batchSize = inputDesc[0].dims.d[0]; + + if (mParam.outputONNXIndices) + { + // ONNX NonMaxSuppression Op Support + const void* const boxesInput = inputs[0]; + const void* const scoresInput = inputs[1]; + + void* nmsIndicesOutput = outputs[0]; + + return EfficientNMSInference(mParam, boxesInput, scoresInput, nullptr, nullptr, nullptr, nullptr, nullptr, + nmsIndicesOutput, workspace, stream); + } + else + { + // Standard NMS Operation + const void* const boxesInput = inputs[0]; + const void* const scoresInput = inputs[1]; + const void* const anchorsInput = mParam.boxDecoder ? inputs[2] : nullptr; + + void* numDetectionsOutput = outputs[0]; + void* nmsBoxesOutput = outputs[1]; + void* nmsScoresOutput = outputs[2]; + void* nmsClassesOutput = outputs[3]; + + return EfficientNMSInference(mParam, boxesInput, scoresInput, anchorsInput, numDetectionsOutput, + nmsBoxesOutput, nmsScoresOutput, nmsClassesOutput, nullptr, workspace, stream); + } + } + catch (const std::exception& e) + { + caughtError(e); + } + return -1; +} + +EfficientNMSPluginCreator::EfficientNMSPluginCreator() + : mParam{} +{ + mPluginAttributes.clear(); + mPluginAttributes.emplace_back(PluginField("score_threshold", nullptr, PluginFieldType::kFLOAT32, 1)); + mPluginAttributes.emplace_back(PluginField("iou_threshold", nullptr, PluginFieldType::kFLOAT32, 1)); + mPluginAttributes.emplace_back(PluginField("max_output_boxes", nullptr, PluginFieldType::kINT32, 1)); + mPluginAttributes.emplace_back(PluginField("background_class", nullptr, PluginFieldType::kINT32, 1)); + mPluginAttributes.emplace_back(PluginField("score_activation", nullptr, PluginFieldType::kINT32, 1)); + mPluginAttributes.emplace_back(PluginField("box_coding", nullptr, PluginFieldType::kINT32, 1)); + mFC.nbFields = mPluginAttributes.size(); + mFC.fields = mPluginAttributes.data(); +} + +const char* EfficientNMSPluginCreator::getPluginName() const noexcept +{ + return EFFICIENT_NMS_PLUGIN_NAME; +} + +const char* EfficientNMSPluginCreator::getPluginVersion() const noexcept +{ + return EFFICIENT_NMS_PLUGIN_VERSION; +} + +const PluginFieldCollection* EfficientNMSPluginCreator::getFieldNames() noexcept +{ + return &mFC; +} + +IPluginV2DynamicExt* EfficientNMSPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept +{ + try + { + const PluginField* fields = fc->fields; + for (int i = 0; i < fc->nbFields; ++i) + { + const char* attrName = fields[i].name; + if (!strcmp(attrName, "score_threshold")) + { + ASSERT(fields[i].type == PluginFieldType::kFLOAT32); + mParam.scoreThreshold = *(static_cast(fields[i].data)); + } + if (!strcmp(attrName, "iou_threshold")) + { + ASSERT(fields[i].type == PluginFieldType::kFLOAT32); + mParam.iouThreshold = *(static_cast(fields[i].data)); + } + if (!strcmp(attrName, "max_output_boxes")) + { + ASSERT(fields[i].type == PluginFieldType::kINT32); + mParam.numOutputBoxes = *(static_cast(fields[i].data)); + } + if (!strcmp(attrName, "background_class")) + { + ASSERT(fields[i].type == PluginFieldType::kINT32); + mParam.backgroundClass = *(static_cast(fields[i].data)); + } + if (!strcmp(attrName, "score_activation")) + { + mParam.scoreSigmoid = *(static_cast(fields[i].data)); + } + if (!strcmp(attrName, "box_coding")) + { + ASSERT(fields[i].type == PluginFieldType::kINT32); + mParam.boxCoding = *(static_cast(fields[i].data)); + } + } + + auto* plugin = new EfficientNMSPlugin(mParam); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; +} + +IPluginV2DynamicExt* EfficientNMSPluginCreator::deserializePlugin( + const char* name, const void* serialData, size_t serialLength) noexcept +{ + try + { + // This object will be deleted when the network is destroyed, which will + // call EfficientNMSPlugin::destroy() + auto* plugin = new EfficientNMSPlugin(serialData, serialLength); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; +} + +EfficientNMSONNXPluginCreator::EfficientNMSONNXPluginCreator() + : mParam{} +{ + mPluginAttributes.clear(); + mPluginAttributes.emplace_back(PluginField("score_threshold", nullptr, PluginFieldType::kFLOAT32, 1)); + mPluginAttributes.emplace_back(PluginField("iou_threshold", nullptr, PluginFieldType::kFLOAT32, 1)); + mPluginAttributes.emplace_back(PluginField("max_output_boxes_per_class", nullptr, PluginFieldType::kINT32, 1)); + mPluginAttributes.emplace_back(PluginField("center_point_box", nullptr, PluginFieldType::kINT32, 1)); + mFC.nbFields = mPluginAttributes.size(); + mFC.fields = mPluginAttributes.data(); +} + +const char* EfficientNMSONNXPluginCreator::getPluginName() const noexcept +{ + return EFFICIENT_NMS_ONNX_PLUGIN_NAME; +} + +const char* EfficientNMSONNXPluginCreator::getPluginVersion() const noexcept +{ + return EFFICIENT_NMS_ONNX_PLUGIN_VERSION; +} + +const PluginFieldCollection* EfficientNMSONNXPluginCreator::getFieldNames() noexcept +{ + return &mFC; +} + +IPluginV2DynamicExt* EfficientNMSONNXPluginCreator::createPlugin( + const char* name, const PluginFieldCollection* fc) noexcept +{ + try + { + const PluginField* fields = fc->fields; + for (int i = 0; i < fc->nbFields; ++i) + { + const char* attrName = fields[i].name; + if (!strcmp(attrName, "score_threshold")) + { + ASSERT(fields[i].type == PluginFieldType::kFLOAT32); + mParam.scoreThreshold = *(static_cast(fields[i].data)); + } + if (!strcmp(attrName, "iou_threshold")) + { + ASSERT(fields[i].type == PluginFieldType::kFLOAT32); + mParam.iouThreshold = *(static_cast(fields[i].data)); + } + if (!strcmp(attrName, "max_output_boxes_per_class")) + { + ASSERT(fields[i].type == PluginFieldType::kINT32); + mParam.numOutputBoxesPerClass = *(static_cast(fields[i].data)); + } + if (!strcmp(attrName, "center_point_box")) + { + ASSERT(fields[i].type == PluginFieldType::kINT32); + mParam.boxCoding = *(static_cast(fields[i].data)); + } + } + + // This enables ONNX compatibility mode + mParam.outputONNXIndices = true; + mParam.numOutputBoxes = mParam.numOutputBoxesPerClass; + + auto* plugin = new EfficientNMSPlugin(mParam); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; +} + +IPluginV2DynamicExt* EfficientNMSONNXPluginCreator::deserializePlugin( + const char* name, const void* serialData, size_t serialLength) noexcept +{ + try + { + // This object will be deleted when the network is destroyed, which will + // call EfficientNMSPlugin::destroy() + auto* plugin = new EfficientNMSPlugin(serialData, serialLength); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; +} diff --git a/plugin/efficientNMSPlugin/efficientNMSPlugin.h b/plugin/efficientNMSPlugin/efficientNMSPlugin.h new file mode 100644 index 00000000..b342b096 --- /dev/null +++ b/plugin/efficientNMSPlugin/efficientNMSPlugin.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ +#ifndef TRT_EFFICIENT_NMS_PLUGIN_H +#define TRT_EFFICIENT_NMS_PLUGIN_H + +#include + +#include "plugin.h" +#include "efficientNMSParameters.h" + + +using namespace nvinfer1::plugin; +namespace nvinfer1 +{ +namespace plugin +{ + +class EfficientNMSPlugin : public IPluginV2DynamicExt +{ +public: + explicit EfficientNMSPlugin(EfficientNMSParameters param); + EfficientNMSPlugin(const void* data, size_t length); + ~EfficientNMSPlugin() override = default; + + // IPluginV2 methods + const char* getPluginType() const noexcept override; + const char* getPluginVersion() const noexcept override; + int getNbOutputs() const noexcept override; + int initialize() noexcept override; + void terminate() noexcept override; + size_t getSerializationSize() const noexcept override; + void serialize(void* buffer) const noexcept override; + void destroy() noexcept override; + void setPluginNamespace(const char* libNamespace) noexcept override; + const char* getPluginNamespace() const noexcept override; + + // IPluginV2Ext methods + nvinfer1::DataType getOutputDataType( + int index, const nvinfer1::DataType* inputType, int nbInputs) const noexcept override; + + // IPluginV2DynamicExt methods + IPluginV2DynamicExt* clone() const noexcept override; + DimsExprs getOutputDimensions( + int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) noexcept override; + bool supportsFormatCombination( + int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept override; + void configurePlugin(const DynamicPluginTensorDesc* in, int nbInputs, const DynamicPluginTensorDesc* out, + int nbOutputs) noexcept override; + size_t getWorkspaceSize(const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, + int nbOutputs) const noexcept override; + int enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, const void* const* inputs, + void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; + +private: + EfficientNMSParameters mParam{}; + std::string mNamespace; +}; + +// Standard NMS Operation +class EfficientNMSPluginCreator : public BaseCreator +{ +public: + EfficientNMSPluginCreator(); + ~EfficientNMSPluginCreator() override = default; + + const char* getPluginName() const noexcept override; + const char* getPluginVersion() const noexcept override; + const PluginFieldCollection* getFieldNames() noexcept override; + + IPluginV2DynamicExt* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; + IPluginV2DynamicExt* deserializePlugin( + const char* name, const void* serialData, size_t serialLength) noexcept override; + +protected: + static PluginFieldCollection mFC; + EfficientNMSParameters mParam; + static std::vector mPluginAttributes; + std::string mPluginName; +}; + +// ONNX NonMaxSuppression Op Support +class EfficientNMSONNXPluginCreator : public BaseCreator +{ +public: + EfficientNMSONNXPluginCreator(); + ~EfficientNMSONNXPluginCreator() override = default; + + const char* getPluginName() const noexcept override; + const char* getPluginVersion() const noexcept override; + const PluginFieldCollection* getFieldNames() noexcept override; + + IPluginV2DynamicExt* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; + IPluginV2DynamicExt* deserializePlugin( + const char* name, const void* serialData, size_t serialLength) noexcept override; + +protected: + static PluginFieldCollection mFC; + EfficientNMSParameters mParam; + static std::vector mPluginAttributes; + std::string mPluginName; +}; + +} // namespace plugin +} // namespace nvinfer1 + +#endif // TRT_EFFICIENT_NMS_PLUGIN_H diff --git a/plugin/embLayerNormPlugin/embLayerNormKernel.cu b/plugin/embLayerNormPlugin/embLayerNormKernel.cu index ea6aad77..76de15e5 100644 --- a/plugin/embLayerNormPlugin/embLayerNormKernel.cu +++ b/plugin/embLayerNormPlugin/embLayerNormKernel.cu @@ -78,7 +78,7 @@ __global__ void fillSBSMaskKernel( inputMaskX[(bi * xmmas_m + mi) * threads_per_cta + tidx] = mask; } -void convertMask(const uint32_t S, const uint32_t B, const uint32_t warps_m, const uint32_t warps_n, +cudaError_t convertMask(const uint32_t S, const uint32_t B, const uint32_t warps_m, const uint32_t warps_n, const uint32_t warps_k, const int* inputMaskSB, uint32_t* inputMaskX, cudaStream_t stream) { const size_t xmmas_m = (S + 16 * warps_m - 1) / (16 * warps_m); @@ -86,7 +86,7 @@ void convertMask(const uint32_t S, const uint32_t B, const uint32_t warps_m, con const size_t threads_per_cta = warps_m * warps_n * warps_k * 32; dim3 grid(xmmas_m, B); fillSBSMaskKernel<<>>(warps_m, warps_n, S, inputMaskSB, inputMaskX); - CHECK(cudaPeekAtLastError()); + return cudaPeekAtLastError(); } template @@ -172,9 +172,7 @@ int computeMaskIdx(cudaStream_t stream, const int S, const int B, const int* mas maskIdxKernel<256><<>>(S, mask, maskIdx); } - CHECK(cudaPeekAtLastError()); - - return 0; + return cudaPeekAtLastError(); } template diff --git a/plugin/embLayerNormPlugin/embLayerNormPlugin.cpp b/plugin/embLayerNormPlugin/embLayerNormPlugin.cpp index 8e59156b..013fe59c 100644 --- a/plugin/embLayerNormPlugin/embLayerNormPlugin.cpp +++ b/plugin/embLayerNormPlugin/embLayerNormPlugin.cpp @@ -31,8 +31,8 @@ namespace bert { namespace { -static const char* EMB_LAYER_NORM_VERSION{"1"}; -static const char* EMB_LAYER_NORM_NAME{"CustomEmbLayerNormPluginDynamic"}; +const char* EMB_LAYER_NORM_VERSION{"1"}; +const char* EMB_LAYER_NORM_NAME{"CustomEmbLayerNormPluginDynamic"}; } // namespace // Static class fields initialization @@ -82,7 +82,7 @@ EmbLayerNormPluginDynamic::EmbLayerNormPluginDynamic(const std::string& name, co , mTokEmbDev(nullptr) , mPosEmbDev(nullptr) { - gLogVerbose << "EmbLayerNormPluginDynamic deserialize\n"; + gLogVerbose << "EmbLayerNormPluginDynamic deserialize." << std::endl; // Deserialize in the same order as serialization deserialize_value(&data, &length, &mType); @@ -110,95 +110,111 @@ EmbLayerNormPluginDynamic::EmbLayerNormPluginDynamic(const std::string& name, co } // IPluginV2DynamicExt Methods -IPluginV2DynamicExt* EmbLayerNormPluginDynamic::clone() const +IPluginV2DynamicExt* EmbLayerNormPluginDynamic::clone() const noexcept { - gLogVerbose << "EmbLayerNormPluginDynamic clone\n"; + try + { + gLogVerbose << "EmbLayerNormPluginDynamic clone." << std::endl; - auto p = new EmbLayerNormPluginDynamic( - mLayerName, mType, mMhaType, mBeta, mGamma, mWordEmb, mPosEmb, mTokEmb, mUseFullMask); - p->mS = mS; - p->setPluginNamespace(mNamespace.c_str()); + auto p = new EmbLayerNormPluginDynamic( + mLayerName, mType, mMhaType, mBeta, mGamma, mWordEmb, mPosEmb, mTokEmb, mUseFullMask); + p->mS = mS; + p->setPluginNamespace(mNamespace.c_str()); - return p; + return p; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } DimsExprs EmbLayerNormPluginDynamic::getOutputDimensions( - int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) + int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) noexcept { - // Input should be input ids and token ids and the input mask - // Output should be the embeddings tensor and mask indices - assert(nbInputs == 3); - - assert(inputs[0].nbDims == 2); // BxS - assert(inputs[0].nbDims == inputs[1].nbDims); - assert(inputs[0].nbDims == inputs[2].nbDims); - - assert(outputIndex == 0 || outputIndex == 1); - - if (outputIndex == 0) + try { - DimsExprs ret; - ret.nbDims = 5; - ret.d[0] = inputs[0].d[0]; - ret.d[1] = inputs[0].d[1]; - ret.d[2] = exprBuilder.constant(mLd); - ret.d[3] = exprBuilder.constant(1); - ret.d[4] = exprBuilder.constant(1); - return ret; - } + // Input should be input ids and token ids and the input mask + // Output should be the embeddings tensor and mask indices + assert(nbInputs == 3); - DimsExprs ret; - ret.nbDims = 2; - ret.d[0] = inputs[0].d[BDIM]; - auto cms0 = exprBuilder.constant(unfusedMaskSize); + assert(inputs[0].nbDims == 2); // BxS + assert(inputs[0].nbDims == inputs[1].nbDims); + assert(inputs[0].nbDims == inputs[2].nbDims); - // this code must match getMHAMaskPackedSize in bertCommon.h - bool isSmOK = (mSM == kSM_75 || mSM == kSM_80 || mSM == kSM_86); - bool isPrecisionOK = (mMhaType == nvinfer1::DataType::kHALF || mMhaType == nvinfer1::DataType::kINT8); - if (mUseFullMask || (isSmOK && isPrecisionOK)) - { - // support 128, 384 in both int8 and fp16 - auto cms128 = exprBuilder.constant(packedMaskSize128); - auto cms384 = exprBuilder.constant(packedMaskSize384); - auto c128 = exprBuilder.constant(128); - auto c384 = exprBuilder.constant(384); - auto is128 = exprBuilder.operation(DimensionOperation::kEQUAL, *inputs[0].d[SDIM], *c128); - auto is384 = exprBuilder.operation(DimensionOperation::kEQUAL, *inputs[0].d[SDIM], *c384); - auto sel128 = exprBuilder.operation(DimensionOperation::kPROD, *is128, *cms128); - auto sel384 = exprBuilder.operation(DimensionOperation::kPROD, *is384, *cms384); - auto maskSize = exprBuilder.operation(DimensionOperation::kSUM, *sel384, *sel128); + assert(outputIndex == 0 || outputIndex == 1); - if (mMhaType == nvinfer1::DataType::kHALF) + if (outputIndex == 0) { - // support 64, 96 only in fp16 - auto cms64 = exprBuilder.constant(packedMaskSize64); - auto cms96 = exprBuilder.constant(packedMaskSize96); - auto c64 = exprBuilder.constant(64); - auto c96 = exprBuilder.constant(96); - - auto is64 = exprBuilder.operation(DimensionOperation::kEQUAL, *inputs[0].d[SDIM], *c64); - auto is96 = exprBuilder.operation(DimensionOperation::kEQUAL, *inputs[0].d[SDIM], *c96); - auto sel64 = exprBuilder.operation(DimensionOperation::kPROD, *is64, *cms64); - auto sel96 = exprBuilder.operation(DimensionOperation::kPROD, *is96, *cms96); - auto maskSize2 = exprBuilder.operation(DimensionOperation::kSUM, *sel64, *sel96); - maskSize = exprBuilder.operation(DimensionOperation::kSUM, *maskSize, *maskSize2); + DimsExprs ret; + ret.nbDims = 5; + ret.d[0] = inputs[0].d[0]; + ret.d[1] = inputs[0].d[1]; + ret.d[2] = exprBuilder.constant(mLd); + ret.d[3] = exprBuilder.constant(1); + ret.d[4] = exprBuilder.constant(1); + return ret; } - auto is0 = exprBuilder.operation(DimensionOperation::kEQUAL, *maskSize, *exprBuilder.constant(0)); - auto sel0 = exprBuilder.operation(DimensionOperation::kPROD, *is0, *cms0); - auto combinedMaskSize = exprBuilder.operation(DimensionOperation::kSUM, *maskSize, *sel0); - ret.d[1] = combinedMaskSize; - } - else - { - ret.d[1] = cms0; - } + DimsExprs ret; + ret.nbDims = 2; + ret.d[0] = inputs[0].d[BDIM]; + auto cms0 = exprBuilder.constant(unfusedMaskSize); - return ret; + // this code must match getMHAMaskPackedSize in bertCommon.h + bool isSmOK = (mSM == kSM_75 || mSM == kSM_80 || mSM == kSM_86); + bool isPrecisionOK = (mMhaType == nvinfer1::DataType::kHALF || mMhaType == nvinfer1::DataType::kINT8); + if (mUseFullMask || (isSmOK && isPrecisionOK)) + { + // support 128, 384 in both int8 and fp16 + auto cms128 = exprBuilder.constant(packedMaskSize128); + auto cms384 = exprBuilder.constant(packedMaskSize384); + auto c128 = exprBuilder.constant(128); + auto c384 = exprBuilder.constant(384); + auto is128 = exprBuilder.operation(DimensionOperation::kEQUAL, *inputs[0].d[SDIM], *c128); + auto is384 = exprBuilder.operation(DimensionOperation::kEQUAL, *inputs[0].d[SDIM], *c384); + auto sel128 = exprBuilder.operation(DimensionOperation::kPROD, *is128, *cms128); + auto sel384 = exprBuilder.operation(DimensionOperation::kPROD, *is384, *cms384); + auto maskSize = exprBuilder.operation(DimensionOperation::kSUM, *sel384, *sel128); + + if (mMhaType == nvinfer1::DataType::kHALF) + { + // support 64, 96 only in fp16 + auto cms64 = exprBuilder.constant(packedMaskSize64); + auto cms96 = exprBuilder.constant(packedMaskSize96); + auto c64 = exprBuilder.constant(64); + auto c96 = exprBuilder.constant(96); + + auto is64 = exprBuilder.operation(DimensionOperation::kEQUAL, *inputs[0].d[SDIM], *c64); + auto is96 = exprBuilder.operation(DimensionOperation::kEQUAL, *inputs[0].d[SDIM], *c96); + auto sel64 = exprBuilder.operation(DimensionOperation::kPROD, *is64, *cms64); + auto sel96 = exprBuilder.operation(DimensionOperation::kPROD, *is96, *cms96); + auto maskSize2 = exprBuilder.operation(DimensionOperation::kSUM, *sel64, *sel96); + maskSize = exprBuilder.operation(DimensionOperation::kSUM, *maskSize, *maskSize2); + } + + auto is0 = exprBuilder.operation(DimensionOperation::kEQUAL, *maskSize, *exprBuilder.constant(0)); + auto sel0 = exprBuilder.operation(DimensionOperation::kPROD, *is0, *cms0); + auto combinedMaskSize = exprBuilder.operation(DimensionOperation::kSUM, *maskSize, *sel0); + ret.d[1] = combinedMaskSize; + } + else + { + ret.d[1] = cms0; + } + + return ret; + } + catch (const std::exception& e) + { + caughtError(e); + } + return DimsExprs{}; } bool EmbLayerNormPluginDynamic::supportsFormatCombination( - int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) + int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept { // 3 inputs of size BxS assert(nbInputs == 3); @@ -232,9 +248,9 @@ bool EmbLayerNormPluginDynamic::supportsFormatCombination( } void EmbLayerNormPluginDynamic::configurePlugin( - const DynamicPluginTensorDesc* inputs, int nbInputs, const DynamicPluginTensorDesc* outputs, int nbOutputs) + const DynamicPluginTensorDesc* inputs, int nbInputs, const DynamicPluginTensorDesc* outputs, int nbOutputs) noexcept { - gLogVerbose << "EmbLayerNormPluginDynamic configurePlugin\n"; + gLogVerbose << "EmbLayerNormPluginDynamic configurePlugin." << std::endl; // Validate input arguments assert(nbOutputs == 2); @@ -286,81 +302,98 @@ void EmbLayerNormPluginDynamic::configurePlugin( } size_t EmbLayerNormPluginDynamic::getWorkspaceSize( - const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const + const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const noexcept { return 0; } int EmbLayerNormPluginDynamic::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { - const int batchSize = inputDesc->dims.d[BDIM]; - const int S = inputDesc->dims.d[SDIM]; - int status = -1; - - // Our plugin outputs only one tensor - const auto inputIds = static_cast(inputs[0]); - const auto segmentIds = static_cast(inputs[1]); - const auto inputMask = static_cast(inputs[2]); - - const float* beta = mBetaDev.get(); - const float* gamma = mGammaDev.get(); - if (mType == DataType::kFLOAT) + try { - auto output = static_cast(outputs[0]); - const auto wordEmb = static_cast(mWordEmbDev.get()); - const auto tokEmb = static_cast(mTokEmbDev.get()); - const auto posEmb = static_cast(mPosEmbDev.get()); - embSkipLayerNorm(stream, static_cast(mLd), batchSize, S, inputIds, segmentIds, beta, gamma, wordEmb, - posEmb, tokEmb, output); - } - else if (mType == DataType::kHALF) - { - auto output = static_cast(outputs[0]); - const auto wordEmb = static_cast(mWordEmbDev.get()); - const auto tokEmb = static_cast(mTokEmbDev.get()); - const auto posEmb = static_cast(mPosEmbDev.get()); - embSkipLayerNorm(stream, static_cast(mLd), batchSize, S, inputIds, segmentIds, beta, gamma, wordEmb, - posEmb, tokEmb, output); - } - else - { - gLogError << "Unsupported type error, expected [kHALF,kFLOAT], but received " << static_cast(mType) - << std::endl; - assert(false); - } + const int batchSize = inputDesc->dims.d[BDIM]; + const int S = inputDesc->dims.d[SDIM]; + int status = STATUS_FAILURE; - CHECK(cudaPeekAtLastError()); + // Our plugin outputs only one tensor + const auto inputIds = static_cast(inputs[0]); + const auto segmentIds = static_cast(inputs[1]); + const auto inputMask = static_cast(inputs[2]); - // check mha use fused kernel - if (mUseFullMask || unfusedMaskSize != getMHAMaskPackedSize(mSM, mMhaType, S)) - { - size_t warps_m = 0, warps_n = 0, warps_k = 1; - if (S == 64 || S == 96 || S == 128) + const float* beta = mBetaDev.get(); + const float* gamma = mGammaDev.get(); + if (mType == DataType::kFLOAT) { - warps_m = 2; - warps_n = 2; + auto output = static_cast(outputs[0]); + const auto wordEmb = static_cast(mWordEmbDev.get()); + const auto tokEmb = static_cast(mTokEmbDev.get()); + const auto posEmb = static_cast(mPosEmbDev.get()); + status = embSkipLayerNorm(stream, static_cast(mLd), batchSize, S, inputIds, segmentIds, beta, gamma, + wordEmb, posEmb, tokEmb, output); + + if (status != cudaSuccess) + { + return status; + } } - else if (S == 384) + else if (mType == DataType::kHALF) { - warps_m = 1; - warps_n = 8; + auto output = static_cast(outputs[0]); + const auto wordEmb = static_cast(mWordEmbDev.get()); + const auto tokEmb = static_cast(mTokEmbDev.get()); + const auto posEmb = static_cast(mPosEmbDev.get()); + status = embSkipLayerNorm(stream, static_cast(mLd), batchSize, S, inputIds, segmentIds, beta, gamma, + wordEmb, posEmb, tokEmb, output); + + if (status != cudaSuccess) + { + return status; + } } - uint32_t* inputMaskX = static_cast(outputs[1]); + else + { + gLogError << "Unsupported type error, expected [kHALF,kFLOAT], but received " << static_cast(mType) + << std::endl; - convertMask(S, batchSize, warps_m, warps_n, warps_k, inputMask, inputMaskX, stream); + return STATUS_NOT_SUPPORTED; + } + + // check mha use fused kernel + if (mUseFullMask || unfusedMaskSize != getMHAMaskPackedSize(mSM, mMhaType, S)) + { + size_t warps_m = 0, warps_n = 0, warps_k = 1; + if (S == 64 || S == 96 || S == 128) + { + warps_m = 2; + warps_n = 2; + } + else if (S == 384) + { + warps_m = 1; + warps_n = 8; + } + uint32_t* inputMaskX = static_cast(outputs[1]); + + status = convertMask(S, batchSize, warps_m, warps_n, warps_k, inputMask, inputMaskX, stream); + } + else + { + int* maskIdx = static_cast(outputs[1]); + status = computeMaskIdx(stream, S, batchSize, inputMask, maskIdx); + } + + return status; } - else + catch (const std::exception& e) { - int* maskIdx = static_cast(outputs[1]); - computeMaskIdx(stream, S, batchSize, inputMask, maskIdx); + caughtError(e); } - - return status; + return STATUS_FAILURE; } // IPluginV2Ext Methods -DataType EmbLayerNormPluginDynamic::getOutputDataType(int index, const DataType* inputTypes, int nbInputs) const +DataType EmbLayerNormPluginDynamic::getOutputDataType(int index, const DataType* inputTypes, int nbInputs) const noexcept { assert(index == 0 || index == 1); @@ -373,32 +406,32 @@ DataType EmbLayerNormPluginDynamic::getOutputDataType(int index, const DataType* } // IPluginV2 Methods -const char* EmbLayerNormPluginDynamic::getPluginType() const +const char* EmbLayerNormPluginDynamic::getPluginType() const noexcept { return EMB_LAYER_NORM_NAME; } -const char* EmbLayerNormPluginDynamic::getPluginVersion() const +const char* EmbLayerNormPluginDynamic::getPluginVersion() const noexcept { return EMB_LAYER_NORM_VERSION; } -int EmbLayerNormPluginDynamic::getNbOutputs() const +int EmbLayerNormPluginDynamic::getNbOutputs() const noexcept { return 2; } -int EmbLayerNormPluginDynamic::initialize() +int EmbLayerNormPluginDynamic::initialize() noexcept { return 0; } -void EmbLayerNormPluginDynamic::terminate() +void EmbLayerNormPluginDynamic::terminate() noexcept { - gLogVerbose << "EmbLayerNormPluginDynamic terminate\n"; + gLogVerbose << "EmbLayerNormPluginDynamic terminate." << std::endl; } -size_t EmbLayerNormPluginDynamic::getSerializationSize() const +size_t EmbLayerNormPluginDynamic::getSerializationSize() const noexcept { const size_t wordSize = getElementSize(mType); return sizeof(mType) // type @@ -413,7 +446,7 @@ size_t EmbLayerNormPluginDynamic::getSerializationSize() const ; } -void EmbLayerNormPluginDynamic::serialize(void* buffer) const +void EmbLayerNormPluginDynamic::serialize(void* buffer) const noexcept { serialize_value(&buffer, mType); serialize_value(&buffer, mMhaType); @@ -434,24 +467,31 @@ void EmbLayerNormPluginDynamic::serialize(void* buffer) const serFromDev(d, static_cast(mTokEmbDev.get()), mLd * mTokVocabSize * wordSize); } -void EmbLayerNormPluginDynamic::destroy() +void EmbLayerNormPluginDynamic::destroy() noexcept { - gLogVerbose << "EmbLayerNormPluginDynamic destroy\n"; + gLogVerbose << "EmbLayerNormPluginDynamic destroy." << std::endl; // This gets called when the network containing plugin is destroyed - mGammaDev.release(); - mBetaDev.release(); - mWordEmbDev.release(); - mPosEmbDev.release(); - mTokEmbDev.release(); + mGammaDev.reset(nullptr); + mBetaDev.reset(nullptr); + mWordEmbDev.reset(nullptr); + mPosEmbDev.reset(nullptr); + mTokEmbDev.reset(nullptr); delete this; } -void EmbLayerNormPluginDynamic::setPluginNamespace(const char* libNamespace) +void EmbLayerNormPluginDynamic::setPluginNamespace(const char* libNamespace) noexcept { - mNamespace = libNamespace; + try + { + mNamespace = libNamespace; + } + catch (const std::exception& e) + { + caughtError(e); + } } -const char* EmbLayerNormPluginDynamic::getPluginNamespace() const +const char* EmbLayerNormPluginDynamic::getPluginNamespace() const noexcept { return mNamespace.c_str(); } @@ -464,116 +504,140 @@ EmbLayerNormPluginDynamicCreator::EmbLayerNormPluginDynamicCreator() mFC.fields = mPluginAttributes.data(); } -const char* EmbLayerNormPluginDynamicCreator::getPluginName() const +const char* EmbLayerNormPluginDynamicCreator::getPluginName() const noexcept { return EMB_LAYER_NORM_NAME; } -const char* EmbLayerNormPluginDynamicCreator::getPluginVersion() const +const char* EmbLayerNormPluginDynamicCreator::getPluginVersion() const noexcept { return EMB_LAYER_NORM_VERSION; } -const PluginFieldCollection* EmbLayerNormPluginDynamicCreator::getFieldNames() +const PluginFieldCollection* EmbLayerNormPluginDynamicCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2* EmbLayerNormPluginDynamicCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2* EmbLayerNormPluginDynamicCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { - gLogVerbose << "EmbLayerNormPluginDynamic createPlugin\n"; - - bool output_fp16 = false; - bool useFullMask = false; - Weights beta; - Weights gamma; - Weights word_emb; - Weights pos_emb; - Weights tok_emb; - int mhaTypeId = 0; - for (int i = 0; i < fc->nbFields; i++) + try { - std::string field_name(fc->fields[i].name); - if (field_name.compare("bert_embeddings_layernorm_beta") == 0) + gLogVerbose << "EmbLayerNormPluginDynamic createPlugin." << std::endl; + + bool output_fp16 = false; + bool useFullMask = false; + Weights beta; + Weights gamma; + Weights word_emb; + Weights pos_emb; + Weights tok_emb; + int mhaTypeId = 0; + for (int i = 0; i < fc->nbFields; i++) { - gLogVerbose << "Building bert_embeddings_layernorm_beta...\n"; - beta.values = fc->fields[i].data; - beta.count = fc->fields[i].length; - beta.type = fieldTypeToDataType(fc->fields[i].type); + std::string field_name(fc->fields[i].name); + if (field_name.compare("bert_embeddings_layernorm_beta") == 0) + { + gLogVerbose << "Building bert_embeddings_layernorm_beta..." << std::endl; + beta.values = fc->fields[i].data; + beta.count = fc->fields[i].length; + beta.type = fieldTypeToDataType(fc->fields[i].type); + } + + if (field_name.compare("bert_embeddings_layernorm_gamma") == 0) + { + gLogVerbose << "Building bert_embeddings_layernorm_gamma..." << std::endl; + gamma.values = fc->fields[i].data; + gamma.count = fc->fields[i].length; + gamma.type = fieldTypeToDataType(fc->fields[i].type); + } + + if (field_name.compare("bert_embeddings_word_embeddings") == 0) + { + gLogVerbose << "Building bert_embeddings_word_embeddings..." << std::endl; + word_emb.values = fc->fields[i].data; + word_emb.count = fc->fields[i].length; + word_emb.type = fieldTypeToDataType(fc->fields[i].type); + } + + if (field_name.compare("bert_embeddings_token_type_embeddings") == 0) + { + gLogVerbose << "Building bert_embeddings_token_type_embeddings..." << std::endl; + tok_emb.values = fc->fields[i].data; + tok_emb.count = fc->fields[i].length; + tok_emb.type = fieldTypeToDataType(fc->fields[i].type); + } + + if (field_name.compare("bert_embeddings_position_embeddings") == 0) + { + gLogVerbose << "Building bert_embeddings_position_embeddings..." << std::endl; + pos_emb.values = fc->fields[i].data; + pos_emb.count = fc->fields[i].length; + pos_emb.type = fieldTypeToDataType(fc->fields[i].type); + } + if (field_name.compare("output_fp16") == 0) + { + gLogVerbose << "Building output_fp16..." << std::endl; + assert(fc->fields[i].type == PluginFieldType::kINT32); + output_fp16 = static_cast(fc->fields[i].data)[0] != 0; + } + if (field_name.compare("full_mask") == 0) + { + gLogVerbose << "Building full_mask..." << std::endl; + assert(fc->fields[i].type == PluginFieldType::kINT32); + useFullMask = static_cast(fc->fields[i].data)[0] != 0; + } + if (field_name.compare("mha_type_id") == 0) + { + mhaTypeId = *static_cast(fc->fields[i].data); + ASSERT(mhaTypeId >= 0 && mhaTypeId <= 3); + gLogVerbose << "Building mha typeId: " << mhaTypeId << std::endl; + } } - if (field_name.compare("bert_embeddings_layernorm_gamma") == 0) - { - gLogVerbose << "Building bert_embeddings_layernorm_gamma...\n"; - gamma.values = fc->fields[i].data; - gamma.count = fc->fields[i].length; - gamma.type = fieldTypeToDataType(fc->fields[i].type); - } - - if (field_name.compare("bert_embeddings_word_embeddings") == 0) - { - gLogVerbose << "Building bert_embeddings_word_embeddings...\n"; - word_emb.values = fc->fields[i].data; - word_emb.count = fc->fields[i].length; - word_emb.type = fieldTypeToDataType(fc->fields[i].type); - } - - if (field_name.compare("bert_embeddings_token_type_embeddings") == 0) - { - gLogVerbose << "Building bert_embeddings_token_type_embeddings...\n"; - tok_emb.values = fc->fields[i].data; - tok_emb.count = fc->fields[i].length; - tok_emb.type = fieldTypeToDataType(fc->fields[i].type); - } - - if (field_name.compare("bert_embeddings_position_embeddings") == 0) - { - gLogVerbose << "Building bert_embeddings_position_embeddings...\n"; - pos_emb.values = fc->fields[i].data; - pos_emb.count = fc->fields[i].length; - pos_emb.type = fieldTypeToDataType(fc->fields[i].type); - } - if (field_name.compare("output_fp16") == 0) - { - gLogVerbose << "Building output_fp16...\n"; - assert(fc->fields[i].type == PluginFieldType::kINT32); - output_fp16 = static_cast(fc->fields[i].data)[0] != 0; - } - if (field_name.compare("full_mask") == 0) - { - gLogVerbose << "Building full_mask...\n"; - assert(fc->fields[i].type == PluginFieldType::kINT32); - useFullMask = static_cast(fc->fields[i].data)[0] != 0; - } - if (field_name.compare("mha_type_id") == 0) - { - mhaTypeId = *static_cast(fc->fields[i].data); - ASSERT(mhaTypeId >= 0 && mhaTypeId <= 3); - gLogVerbose << "Building mha typeId: " << mhaTypeId << std::endl; - } + gLogVerbose << "Building the Plugin..." << std::endl; + DataType mhaType = static_cast(mhaTypeId); + EmbLayerNormPluginDynamic* p + = new EmbLayerNormPluginDynamic(name, output_fp16 ? DataType::kHALF : DataType::kFLOAT, mhaType, beta, + gamma, word_emb, pos_emb, tok_emb, useFullMask); + return p; } - - gLogVerbose << "Building the Plugin...\n"; - DataType mhaType = static_cast(mhaTypeId); - EmbLayerNormPluginDynamic* p = new EmbLayerNormPluginDynamic(name, output_fp16 ? DataType::kHALF : DataType::kFLOAT, - mhaType, beta, gamma, word_emb, pos_emb, tok_emb, useFullMask); - return p; + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } IPluginV2* EmbLayerNormPluginDynamicCreator::deserializePlugin( - const char* name, const void* serialData, size_t serialLength) + const char* name, const void* serialData, size_t serialLength) noexcept { - // This object will be deleted when the network is destroyed, which will - // call EmbLayerNormPluginDynamic::destroy() - return new EmbLayerNormPluginDynamic(name, serialData, serialLength); + try + { + // This object will be deleted when the network is destroyed, which will + // call EmbLayerNormPluginDynamic::destroy() + return new EmbLayerNormPluginDynamic(name, serialData, serialLength); + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } -void EmbLayerNormPluginDynamicCreator::setPluginNamespace(const char* libNamespace) +void EmbLayerNormPluginDynamicCreator::setPluginNamespace(const char* libNamespace) noexcept { - mNamespace = libNamespace; + try + { + mNamespace = libNamespace; + } + catch (const std::exception& e) + { + caughtError(e); + } } -const char* EmbLayerNormPluginDynamicCreator::getPluginNamespace() const +const char* EmbLayerNormPluginDynamicCreator::getPluginNamespace() const noexcept { return mNamespace.c_str(); } diff --git a/plugin/embLayerNormPlugin/embLayerNormPlugin.h b/plugin/embLayerNormPlugin/embLayerNormPlugin.h index 2a0f8d65..78c3631b 100644 --- a/plugin/embLayerNormPlugin/embLayerNormPlugin.h +++ b/plugin/embLayerNormPlugin/embLayerNormPlugin.h @@ -35,7 +35,7 @@ template int embSkipLayerNorm(cudaStream_t stream, int ld, int B, int S, const int* inputIds, const int* token_ids, const float* beta, const float* gamma, const T* wordEmb, const T* posEmb, const T* tokEmb, T* output); -void convertMask(const uint32_t S, const uint32_t B, const uint32_t warps_m, const uint32_t warps_n, +cudaError_t convertMask(const uint32_t S, const uint32_t B, const uint32_t warps_m, const uint32_t warps_n, const uint32_t warps_k, const int* inputMaskSB, uint32_t* inputMaskX, cudaStream_t stream); class EmbLayerNormPluginDynamic : public nvinfer1::IPluginV2DynamicExt @@ -52,32 +52,32 @@ public: EmbLayerNormPluginDynamic() = delete; // IPluginV2DynamicExt Methods - nvinfer1::IPluginV2DynamicExt* clone() const override; + nvinfer1::IPluginV2DynamicExt* clone() const noexcept override; nvinfer1::DimsExprs getOutputDimensions( - int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) override; + int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) noexcept override; bool supportsFormatCombination( - int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) override; + int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept override; void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, - const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) override; + const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) noexcept override; size_t getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int nbInputs, - const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const override; + const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const noexcept override; int enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) override; + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; // IPluginV2Ext Methods - nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept override; // IPluginV2 Methods - const char* getPluginType() const override; - const char* getPluginVersion() const override; - int getNbOutputs() const override; - int initialize() override; - void terminate() override; - size_t getSerializationSize() const override; - void serialize(void* buffer) const override; - void destroy() override; - void setPluginNamespace(const char* pluginNamespace) override; - const char* getPluginNamespace() const override; + const char* getPluginType() const noexcept override; + const char* getPluginVersion() const noexcept override; + int getNbOutputs() const noexcept override; + int initialize() noexcept override; + void terminate() noexcept override; + size_t getSerializationSize() const noexcept override; + void serialize(void* buffer) const noexcept override; + void destroy() noexcept override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; + const char* getPluginNamespace() const noexcept override; private: const std::string mLayerName; @@ -102,16 +102,6 @@ private: bool mUseFullMask; nvinfer1::DataType mMhaType; int mSM; - -protected: - // To prevent compiler warnings. - using nvinfer1::IPluginV2DynamicExt::getOutputDimensions; - using nvinfer1::IPluginV2DynamicExt::isOutputBroadcastAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::canBroadcastInputAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::supportsFormat; - using nvinfer1::IPluginV2DynamicExt::configurePlugin; - using nvinfer1::IPluginV2DynamicExt::getWorkspaceSize; - using nvinfer1::IPluginV2DynamicExt::enqueue; }; class EmbLayerNormPluginDynamicCreator : public nvinfer1::IPluginCreator @@ -119,19 +109,19 @@ class EmbLayerNormPluginDynamicCreator : public nvinfer1::IPluginCreator public: EmbLayerNormPluginDynamicCreator(); - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const nvinfer1::PluginFieldCollection* getFieldNames() override; + const nvinfer1::PluginFieldCollection* getFieldNames() noexcept override; - nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) override; + nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) noexcept override; - nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; private: static nvinfer1::PluginFieldCollection mFC; diff --git a/plugin/embLayerNormPlugin/embLayerNormVarSeqlenKernel.cu b/plugin/embLayerNormPlugin/embLayerNormVarSeqlenKernelHFace.cu similarity index 64% rename from plugin/embLayerNormPlugin/embLayerNormVarSeqlenKernel.cu rename to plugin/embLayerNormPlugin/embLayerNormVarSeqlenKernelHFace.cu index 94951c02..f9a6ec66 100644 --- a/plugin/embLayerNormPlugin/embLayerNormVarSeqlenKernel.cu +++ b/plugin/embLayerNormPlugin/embLayerNormVarSeqlenKernelHFace.cu @@ -22,7 +22,7 @@ #include "NvInfer.h" #include "bertCommon.h" #include "common.cuh" -#include "embLayerNormPlugin.h" +#include "plugin.h" #include "serialize.hpp" using namespace nvinfer1; @@ -31,10 +31,10 @@ namespace bert { __global__ void cuSeqlensToPackedMaskKernel( - const uint32_t warps_m, const uint32_t warps_n, const uint32_t S, const int* cuSeqlens, uint32_t* inputMaskX) + const uint32_t warps_m, const uint32_t warps_n, const uint32_t S, const int32_t* cuSeqlens, uint32_t* inputMaskX) { - extern __shared__ int shm_mask[]; // S mask elements of this batch + extern __shared__ int32_t shm_mask[]; // S mask elements of this batch const size_t xmmas_n = (S + 16 * warps_n - 1) / (16 * warps_n); const uint32_t threads_per_cta = blockDim.x; @@ -64,7 +64,7 @@ __global__ void cuSeqlensToPackedMaskKernel( for (size_t ni = 0; ni < xmmas_n; ++ni) { - const int offset = ni * 16 * warps_n + col; + const int32_t offset = ni * 16 * warps_n + col; mask |= (shm_mask[offset + 0] == 1 ? 1u : 0u) << (8 * ni + 0); mask |= (shm_mask[offset + 1] == 1 ? 1u : 0u) << (8 * ni + 1); mask |= (shm_mask[offset + 0] == 1 ? 1u : 0u) << (8 * ni + 2); @@ -79,45 +79,46 @@ __global__ void cuSeqlensToPackedMaskKernel( } void cuSeqlensToPackedMask(const uint32_t S, const uint32_t B, const uint32_t warps_m, const uint32_t warps_n, - const uint32_t warps_k, const int* cuSeqlens, uint32_t* inputMaskX, cudaStream_t stream) + const uint32_t warps_k, const int32_t* cuSeqlens, uint32_t* inputMaskX, cudaStream_t stream) { const size_t xmmas_m = (S + 16 * warps_m - 1) / (16 * warps_m); const size_t threads_per_cta = warps_m * warps_n * warps_k * 32; dim3 grid(xmmas_m, B); - cuSeqlensToPackedMaskKernel<<>>( + cuSeqlensToPackedMaskKernel<<>>( warps_m, warps_n, S, cuSeqlens, inputMaskX); CHECK(cudaPeekAtLastError()); } template -__global__ void embLayerNormKernelVarSeqlen(int ld, const uint32_t* cuSeqlens, const int* inputIds, - const int* segmentIds, const T* beta, const T* gamma, const T* tokEmb, const T* posEmb, const T* segEmb, T* output) +__global__ void embLayerNormKernelVarSeqlenHFace(int32_t ld, const uint32_t* cuSeqlens, const int32_t* inputIds, + const int32_t* segmentIds, const T* beta, const T* gamma, const T* tokEmb, const T* posEmb, const T* segEmb, + T* output) { using BlockReduce = cub::BlockReduce, TPB>; __shared__ typename BlockReduce::TempStorage temp_storage; - const int b = blockIdx.x; - const int s = blockIdx.y; + const int32_t b = blockIdx.x; + const int32_t s = blockIdx.y; - const int sum_s = cuSeqlens[b]; - const int s_b = cuSeqlens[b + 1] - sum_s; + const int32_t sum_s = cuSeqlens[b]; + const int32_t s_b = cuSeqlens[b + 1] - sum_s; // either the whole CTA has work or not if (s >= s_b) return; - const int inOffset = (sum_s + s); - const int outOffset = (sum_s + s) * ld; + const int32_t inOffset = (sum_s + s); + const int32_t outOffset = (sum_s + s) * ld; // 1. lookup word and token of the block // blockIdx.x = position in the sequence // blockIdx.y = batch // gridDim.x = S // gridDim.y = B - __shared__ int inputId; - __shared__ int segmentId; + __shared__ int32_t inputId; + __shared__ int32_t segmentId; if (threadIdx.x == 0) { @@ -128,14 +129,14 @@ __global__ void embLayerNormKernelVarSeqlen(int ld, const uint32_t* cuSeqlens, c // 2. load pos/tok/word embeddings and add them toghether // offset into embeddings is given by wordId * hidden_size - const int poffset = s * ld; - const int ioffset = inputId * ld; - const int soffset = segmentId * ld; + const int32_t poffset = s * ld; + const int32_t ioffset = inputId * ld; + const int32_t soffset = segmentId * ld; // 16B per thread: 8 elements. there should be ld / VPT threads per CTA // 1024: 128 threads // 768: 96 threads - const int toffset = threadIdx.x * VPT; + const int32_t toffset = threadIdx.x * VPT; // 4 * 1024 * 4 * 2 Bytes = 16KB per block T i_local[VPT]; T s_local[VPT]; @@ -150,7 +151,7 @@ __global__ void embLayerNormKernelVarSeqlen(int ld, const uint32_t* cuSeqlens, c const T rld = T(1) / T(ld); #pragma unroll - for (int it = 0; it < VPT; it++) + for (int32_t it = 0; it < VPT; it++) { i_local[it] += s_local[it] + p_local[it]; const T tmp = rld * i_local[it]; @@ -175,7 +176,7 @@ __global__ void embLayerNormKernelVarSeqlen(int ld, const uint32_t* cuSeqlens, c __syncthreads(); ///* #pragma unroll - for (int it = 0; it < VPT; it++) + for (int32_t it = 0; it < VPT; it++) { i_local[it] = s_local[it] * (i_local[it] - mu) * rsigma + p_local[it]; } @@ -185,26 +186,27 @@ __global__ void embLayerNormKernelVarSeqlen(int ld, const uint32_t* cuSeqlens, c } template -int embSkipLayerNormVarSeqlen(cudaStream_t stream, int ld, int B, int S, const uint32_t* cuSeqlens, const int* inputIds, - const int* token_ids, const T* beta, const T* gamma, const T* wordEmb, const T* posEmb, const T* tokEmb, T* output) +int32_t embSkipLayerNormVarSeqlenHFace(cudaStream_t stream, int32_t ld, int32_t B, int32_t S, const uint32_t* cuSeqlens, + const int32_t* inputIds, const int32_t* token_ids, const T* beta, const T* gamma, const T* wordEmb, const T* posEmb, + const T* tokEmb, T* output) { const dim3 grid(B, S, 1); if (ld == 1024) { - constexpr int VPT = 16 / sizeof(T); - constexpr int TPB = 1024 / VPT; + constexpr int32_t VPT = 16 / sizeof(T); + constexpr int32_t TPB = 1024 / VPT; const dim3 block(TPB, 1, 1); - embLayerNormKernelVarSeqlen<<>>( + embLayerNormKernelVarSeqlenHFace<<>>( ld, cuSeqlens, inputIds, token_ids, beta, gamma, wordEmb, posEmb, tokEmb, output); } else if (ld == 768) { - constexpr int VPT = 16 / sizeof(T); - constexpr int TPB = 768 / VPT; + constexpr int32_t VPT = 16 / sizeof(T); + constexpr int32_t TPB = 768 / VPT; const dim3 block(TPB, 1, 1); - embLayerNormKernelVarSeqlen<<>>( + embLayerNormKernelVarSeqlenHFace<<>>( ld, cuSeqlens, inputIds, token_ids, beta, gamma, wordEmb, posEmb, tokEmb, output); } else @@ -217,17 +219,18 @@ int embSkipLayerNormVarSeqlen(cudaStream_t stream, int ld, int B, int S, const u return 0; } -template int embSkipLayerNormVarSeqlen(cudaStream_t, int, int, int, const uint32_t*, const int*, const int*, - const float*, const float*, const float*, const float*, const float*, float*); +template int32_t embSkipLayerNormVarSeqlenHFace(cudaStream_t, int32_t, int32_t, int32_t, const uint32_t*, + const int32_t*, const int32_t*, const float*, const float*, const float*, const float*, const float*, float*); -template int embSkipLayerNormVarSeqlen(cudaStream_t, int, int, int, const uint32_t*, const int*, const int*, - const half*, const half*, const half*, const half*, const half*, half*); +template int32_t embSkipLayerNormVarSeqlenHFace(cudaStream_t, int32_t, int32_t, int32_t, const uint32_t*, + const int32_t*, const int32_t*, const half*, const half*, const half*, const half*, const half*, half*); /// REDO BASED ON OLD KERNEL TO REPRODUCE EXACT RESULTS template -__global__ void embLayerNormKernel2(int ld, const int* inputIds, const int* tokenIds, const int* cuSeqlens, - const float* beta, const float* gamma, const T* wordEmb, const T* posEmb, const T* tokEmb, T* output) +__global__ void embLayerNormKernelHFace(int32_t ld, const int32_t* inputIds, const int32_t* tokenIds, + const int32_t* cuSeqlens, const float* beta, const float* gamma, const T* wordEmb, const T* posEmb, const T* tokEmb, + T* output) { // this code currently assumes the input shape is SxB, row-major => seqPos = s * B + b // instead we want BxS, row-major => seqPos = b * S + s @@ -238,22 +241,22 @@ __global__ void embLayerNormKernel2(int ld, const int* inputIds, const int* toke // blockIdx.y = batch // gridDim.x = S // gridDim.y = B - const int s = blockIdx.x; - const int b = blockIdx.y; + const int32_t s = blockIdx.x; + const int32_t b = blockIdx.y; - const int sumS = cuSeqlens[b]; - const int s_b = cuSeqlens[b + 1] - sumS; + const int32_t sumS = cuSeqlens[b]; + const int32_t s_b = cuSeqlens[b + 1] - sumS; if (s >= s_b) return; // This CTA has nothing to do - __shared__ int wordId; - __shared__ int tokenId; + __shared__ int32_t wordId; + __shared__ int32_t tokenId; const T rld = T(1.f) / T(ld); // seqPos = b + s * B - // const int seqPos = blockIdx.y + blockIdx.x * gridDim.y; + // const int32_t seqPos = blockIdx.y + blockIdx.x * gridDim.y; - // const int seqPos = s * B + s; - const int seqPos = sumS + s; + // const int32_t seqPos = s * B + s; + const int32_t seqPos = sumS + s; if (threadIdx.x == 0) { wordId = inputIds[seqPos]; @@ -263,15 +266,15 @@ __global__ void embLayerNormKernel2(int ld, const int* inputIds, const int* toke // 2. load pos/tok/word embeddings and add them toghether // offset into embeddings is given by wordId * hidden_size - const int poffset = blockIdx.x * ld; - const int woffset = wordId * ld; - const int toffset = tokenId * ld; + const int32_t poffset = blockIdx.x * ld; + const int32_t woffset = wordId * ld; + const int32_t toffset = tokenId * ld; // the output offset is given by b * (S*hidden_size) + s * hidden_size - const int outOffset = seqPos * ld; + const int32_t outOffset = seqPos * ld; kvp threadData(0, 0); - for (int it = threadIdx.x; it < ld; it += TPB) + for (int32_t it = threadIdx.x; it < ld; it += TPB) { const T w(wordEmb[woffset + it]); const T t(tokEmb[toffset + it]); @@ -288,26 +291,24 @@ __global__ void embLayerNormKernel2(int ld, const int* inputIds, const int* toke } template -int embSkipLayerNorm2(cudaStream_t stream, int ld, int B, int S, const int* inputIds, const int* tokenIds, - const int* cuSeqlens, const float* beta, const float* gamma, const T* wordEmb, const T* posEmb, const T* tokEmb, - T* output) +int32_t embSkipLayerNormHFace(cudaStream_t stream, int32_t ld, int32_t B, int32_t S, const int32_t* inputIds, + const int32_t* tokenIds, const int32_t* cuSeqlens, const float* beta, const float* gamma, const T* wordEmb, + const T* posEmb, const T* tokEmb, T* output) { - constexpr int tpb = 256; + constexpr int32_t tpb = 256; const dim3 grid(S, B, 1); const dim3 block(tpb, 1, 1); - embLayerNormKernel2 + embLayerNormKernelHFace <<>>(ld, inputIds, tokenIds, cuSeqlens, beta, gamma, wordEmb, posEmb, tokEmb, output); - CHECK(cudaPeekAtLastError()); - - return 0; + return cudaPeekAtLastError(); } -template int embSkipLayerNorm2(cudaStream_t, int, int, int, const int*, const int*, const int*, const float*, - const float*, const float*, const float*, const float*, float*); +template int32_t embSkipLayerNormHFace(cudaStream_t, int32_t, int32_t, int32_t, const int32_t*, const int32_t*, + const int32_t*, const float*, const float*, const float*, const float*, const float*, float*); -template int embSkipLayerNorm2(cudaStream_t, int, int, int, const int*, const int*, const int*, const float*, - const float*, const half*, const half*, const half*, half*); +template int32_t embSkipLayerNormHFace(cudaStream_t, int32_t, int32_t, int32_t, const int32_t*, const int32_t*, + const int32_t*, const float*, const float*, const half*, const half*, const half*, half*); } // namespace bert diff --git a/plugin/embLayerNormPlugin/embLayerNormVarSeqlenKernelMTron.cu b/plugin/embLayerNormPlugin/embLayerNormVarSeqlenKernelMTron.cu new file mode 100644 index 00000000..ffe5a3c7 --- /dev/null +++ b/plugin/embLayerNormPlugin/embLayerNormVarSeqlenKernelMTron.cu @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * + * 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 +#include +#include +#include + +#include "NvInfer.h" +#include "bertCommon.h" +#include "common.cuh" +#include "plugin.h" +#include "serialize.hpp" + +using namespace nvinfer1; + +namespace bert +{ + +template +__global__ void embLayerNormKernelVarSeqlenMTron(int ld, const uint32_t* cuSeqlens, const int* inputIds, + const int* segmentIds, const T* beta, const T* gamma, const T* tokEmb, const T* posEmb, const T* segEmb, T* output, + T* skip) +{ + + using BlockReduce = cub::BlockReduce, TPB>; + __shared__ typename BlockReduce::TempStorage temp_storage; + + const int b = blockIdx.x; + const int s = blockIdx.y; + + const int sum_s = cuSeqlens[b]; + const int s_b = cuSeqlens[b + 1] - sum_s; + + // either the whole CTA has work or not + if (s >= s_b) + return; + + const int inOffset = (sum_s + s); + const int outOffset = (sum_s + s) * ld; + + // 1. lookup word and token of the block + // blockIdx.x = position in the sequence + // blockIdx.y = batch + // gridDim.x = S + // gridDim.y = B + __shared__ int inputId; + __shared__ int segmentId; + + if (threadIdx.x == 0) + { + inputId = inputIds[inOffset]; + segmentId = segmentIds[inOffset]; + } + __syncthreads(); + + // 2. load pos/tok/word embeddings and add them toghether + // offset into embeddings is given by wordId * hidden_size + const int poffset = s * ld; + const int ioffset = inputId * ld; + const int soffset = segmentId * ld; + + // 16B per thread: 8 elements. there should be ld / VPT threads per CTA + // 1024: 128 threads + // 768: 96 threads + const int toffset = threadIdx.x * VPT; + // 4 * 1024 * 4 * 2 Bytes = 16KB per block + T i_local[VPT]; + T s_local[VPT]; + T p_local[VPT]; + + // read embeddings + copy(&tokEmb[ioffset + toffset], i_local); + copy(&segEmb[soffset + toffset], s_local); + copy(&posEmb[poffset + toffset], p_local); + T local = 0.f; + T local2 = 0.f; + + const T rld = T(1) / T(ld); +#pragma unroll + for (int it = 0; it < VPT; it++) + { + i_local[it] += s_local[it] + p_local[it]; + const T tmp = rld * i_local[it]; + local += tmp; + local2 += tmp * i_local[it]; + } + + // load params + copy(i_local, &skip[outOffset + toffset]); + copy(&beta[toffset], p_local); + copy(&gamma[toffset], s_local); + + __shared__ T mu; // mean + __shared__ T rsigma; // 1 / std.dev. + + const auto sumKV = BlockReduce(temp_storage).Reduce(kvp(local, local2), cub::Sum()); + + if (threadIdx.x == 0) + { + mu = sumKV.key; + rsigma = rsqrt(sumKV.value - mu * mu); + } + __syncthreads(); + ///* +#pragma unroll + for (int it = 0; it < VPT; it++) + { + i_local[it] = s_local[it] * (i_local[it] - mu) * rsigma + p_local[it]; + } + /* */ + + copy(i_local, &output[outOffset + toffset]); +} + +template +int embSkipLayerNormVarSeqlenMTron(cudaStream_t stream, int ld, int B, int S, const uint32_t* cuSeqlens, + const int* inputIds, const int* token_ids, const T* beta, const T* gamma, const T* wordEmb, const T* posEmb, + const T* tokEmb, T* output, T* skip) +{ + + const dim3 grid(B, S, 1); + + if (ld == 1024) + { + constexpr int VPT = 16 / sizeof(T); + constexpr int TPB = 1024 / VPT; + const dim3 block(TPB, 1, 1); + embLayerNormKernelVarSeqlenMTron<<>>( + ld, cuSeqlens, inputIds, token_ids, beta, gamma, wordEmb, posEmb, tokEmb, output, skip); + } + else if (ld == 768) + { + constexpr int VPT = 16 / sizeof(T); + constexpr int TPB = 768 / VPT; + const dim3 block(TPB, 1, 1); + embLayerNormKernelVarSeqlenMTron<<>>( + ld, cuSeqlens, inputIds, token_ids, beta, gamma, wordEmb, posEmb, tokEmb, output, skip); + } + else + { + assert(false && "Unsupported hidden dimension"); + } + + CHECK(cudaPeekAtLastError()); + + return 0; +} + +template int embSkipLayerNormVarSeqlenMTron(cudaStream_t, int, int, int, const uint32_t*, const int*, const int*, + const float*, const float*, const float*, const float*, const float*, float*, float*); + +template int embSkipLayerNormVarSeqlenMTron(cudaStream_t, int, int, int, const uint32_t*, const int*, const int*, + const half*, const half*, const half*, const half*, const half*, half*, half*); + +/// REDO BASED ON OLD KERNEL TO REPRODUCE EXACT RESULTS + +template +__global__ void embLayerNormKernelMTron(int ld, const int* inputIds, const int* tokenIds, const int* cuSeqlens, + const float* beta, const float* gamma, const T* wordEmb, const T* posEmb, const T* tokEmb, T* output, T* skip) +{ + // this code currently assumes the input shape is SxB, row-major => seqPos = s * B + b + // instead we want BxS, row-major => seqPos = b * S + s + + cub::Sum pairSum; + // 1. lookup word and token of the block + // blockIdx.x = position in the sequence + // blockIdx.y = batch + // gridDim.x = S + // gridDim.y = B + const int s = blockIdx.x; + const int b = blockIdx.y; + + const int sumS = cuSeqlens[b]; + const int s_b = cuSeqlens[b + 1] - sumS; + if (s >= s_b) + return; // This CTA has nothing to do + __shared__ int wordId; + __shared__ int tokenId; + + const T rld = T(1.f) / T(ld); + // seqPos = b + s * B + // const int seqPos = blockIdx.y + blockIdx.x * gridDim.y; + + // const int seqPos = s * B + s; + const int seqPos = sumS + s; + if (threadIdx.x == 0) + { + wordId = inputIds[seqPos]; + tokenId = tokenIds[seqPos]; + } + __syncthreads(); + + // 2. load pos/tok/word embeddings and add them toghether + // offset into embeddings is given by wordId * hidden_size + const int poffset = blockIdx.x * ld; + const int woffset = wordId * ld; + const int toffset = tokenId * ld; + // the output offset is given by b * (S*hidden_size) + s * hidden_size + const int outOffset = seqPos * ld; + + kvp threadData(0, 0); + + for (int it = threadIdx.x; it < ld; it += TPB) + { + const T w(wordEmb[woffset + it]); + const T t(tokEmb[toffset + it]); + const T p(posEmb[poffset + it]); + const T val = w + t + p; + + output[outOffset + it] = val; + skip[outOffset + it] = val; + const T rldval = rld * val; + threadData = pairSum(threadData, kvp(rldval, rldval * val)); + } + + // 3. layer norm on the sum + layerNorm(threadData, ld, outOffset, beta, gamma, output); +} + +template +int embSkipLayerNormMTron(cudaStream_t stream, int ld, int B, int S, const int* inputIds, const int* tokenIds, + const int* cuSeqlens, const float* beta, const float* gamma, const T* wordEmb, const T* posEmb, const T* tokEmb, + T* output, T* skip) +{ + + constexpr int tpb = 256; + const dim3 grid(S, B, 1); + const dim3 block(tpb, 1, 1); + + embLayerNormKernelMTron<<>>( + ld, inputIds, tokenIds, cuSeqlens, beta, gamma, wordEmb, posEmb, tokEmb, output, skip); + return cudaPeekAtLastError(); +} + +template int embSkipLayerNormMTron(cudaStream_t, int, int, int, const int*, const int*, const int*, const float*, + const float*, const float*, const float*, const float*, float*, float*); + +template int embSkipLayerNormMTron(cudaStream_t, int, int, int, const int*, const int*, const int*, const float*, + const float*, const half*, const half*, const half*, half*, half*); + +} // namespace bert diff --git a/plugin/embLayerNormPlugin/embLayerNormVarSeqlenPlugin.cpp b/plugin/embLayerNormPlugin/embLayerNormVarSeqlenPlugin.cpp index 5e4dd761..d03738ff 100644 --- a/plugin/embLayerNormPlugin/embLayerNormVarSeqlenPlugin.cpp +++ b/plugin/embLayerNormPlugin/embLayerNormVarSeqlenPlugin.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include #include +#include #include #include "NvInfer.h" @@ -44,17 +44,19 @@ constexpr size_t packedMaskSize384 = xmmasM384 * threadsPerCta384; namespace { -static const char* EMB_LAYER_NORM_VAR_SEQLEN_VERSION{"2"}; -static const char* EMB_LAYER_NORM_VAR_SEQLEN_NAME{"CustomEmbLayerNormPluginDynamic"}; +const char* EMB_LAYER_NORM_VAR_SEQLEN_VERSION_HFACE{"2"}; +const char* EMB_LAYER_NORM_VAR_SEQLEN_VERSION_MTRON{"3"}; +const char* EMB_LAYER_NORM_VAR_SEQLEN_NAME{"CustomEmbLayerNormPluginDynamic"}; } // namespace // Static class fields initialization -PluginFieldCollection EmbLayerNormVarSeqlenPluginCreator::mFC{}; -std::vector EmbLayerNormVarSeqlenPluginCreator::mPluginAttributes; +PluginFieldCollection EmbLayerNormVarSeqlenPluginBaseCreator::mFC{}; +std::vector EmbLayerNormVarSeqlenPluginBaseCreator::mPluginAttributes; -REGISTER_TENSORRT_PLUGIN(EmbLayerNormVarSeqlenPluginCreator); +REGISTER_TENSORRT_PLUGIN(EmbLayerNormVarSeqlenPluginHFaceCreator); +REGISTER_TENSORRT_PLUGIN(EmbLayerNormVarSeqlenPluginMTronCreator); -EmbLayerNormVarSeqlenPlugin::EmbLayerNormVarSeqlenPlugin(const std::string& name, const DataType type, +EmbLayerNormVarSeqlenPluginBase::EmbLayerNormVarSeqlenPluginBase(const std::string& name, const DataType type, const Weights& beta, const Weights& gamma, const Weights& wordEmb, const Weights& posEmb, const Weights& tokEmb) : mLayerName(name) , mLd(beta.count) @@ -84,7 +86,8 @@ EmbLayerNormVarSeqlenPlugin::EmbLayerNormVarSeqlenPlugin(const std::string& name copyToDevice(mTokEmb, getWeightsSize(mTokEmb, mType), mTokEmbDev); } -EmbLayerNormVarSeqlenPlugin::EmbLayerNormVarSeqlenPlugin(const std::string& name, const void* data, size_t length) +EmbLayerNormVarSeqlenPluginBase::EmbLayerNormVarSeqlenPluginBase( + const std::string& name, const void* data, size_t length) : mLayerName(name) , mGammaDev(nullptr) , mBetaDev(nullptr) @@ -92,8 +95,6 @@ EmbLayerNormVarSeqlenPlugin::EmbLayerNormVarSeqlenPlugin(const std::string& name , mTokEmbDev(nullptr) , mPosEmbDev(nullptr) { - gLogVerbose << "EmbLayerNormVarSeqlenPlugin deserialize\n"; - // Deserialize in the same order as serialization deserialize_value(&data, &length, &mType); deserialize_value(&data, &length, &mLd); @@ -117,19 +118,55 @@ EmbLayerNormVarSeqlenPlugin::EmbLayerNormVarSeqlenPlugin(const std::string& name copyToDevice(mTokEmb, getWeightsSize(mTokEmb, mType), mTokEmbDev); } -// IPluginV2DynamicExt Methods -IPluginV2DynamicExt* EmbLayerNormVarSeqlenPlugin::clone() const +EmbLayerNormVarSeqlenPluginHFace::EmbLayerNormVarSeqlenPluginHFace(const std::string& name, const DataType type, + const Weights& beta, const Weights& gamma, const Weights& wordEmb, const Weights& posEmb, const Weights& tokEmb) + : EmbLayerNormVarSeqlenPluginBase(name, type, beta, gamma, wordEmb, posEmb, tokEmb) { - gLogVerbose << "EmbLayerNormVarSeqlenPlugin clone\n"; +} - auto p = new EmbLayerNormVarSeqlenPlugin(mLayerName, mType, mBeta, mGamma, mWordEmb, mPosEmb, mTokEmb); +EmbLayerNormVarSeqlenPluginHFace::EmbLayerNormVarSeqlenPluginHFace( + const std::string& name, const void* data, size_t length) + : EmbLayerNormVarSeqlenPluginBase(name, data, length) +{ + gLogVerbose << "EmbLayerNormVarSeqlenPluginHFace deserialize\n"; +} + +EmbLayerNormVarSeqlenPluginMTron::EmbLayerNormVarSeqlenPluginMTron(const std::string& name, const DataType type, + const Weights& beta, const Weights& gamma, const Weights& wordEmb, const Weights& posEmb, const Weights& tokEmb) + : EmbLayerNormVarSeqlenPluginBase(name, type, beta, gamma, wordEmb, posEmb, tokEmb) +{ +} + +EmbLayerNormVarSeqlenPluginMTron::EmbLayerNormVarSeqlenPluginMTron( + const std::string& name, const void* data, size_t length) + : EmbLayerNormVarSeqlenPluginBase(name, data, length) +{ + gLogVerbose << "EmbLayerNormVarSeqlenPluginMTron deserialize\n"; +} + +// IPluginV2DynamicExt Methods +IPluginV2DynamicExt* EmbLayerNormVarSeqlenPluginHFace::clone() const noexcept +{ + gLogVerbose << "EmbLayerNormVarSeqlenPluginHFace clone\n"; + + auto p = new EmbLayerNormVarSeqlenPluginHFace(mLayerName, mType, mBeta, mGamma, mWordEmb, mPosEmb, mTokEmb); p->setPluginNamespace(mNamespace.c_str()); return p; } -DimsExprs EmbLayerNormVarSeqlenPlugin::getOutputDimensions( - int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) +IPluginV2DynamicExt* EmbLayerNormVarSeqlenPluginMTron::clone() const noexcept +{ + gLogVerbose << "EmbLayerNormVarSeqlenPluginMTron clone\n"; + + auto p = new EmbLayerNormVarSeqlenPluginMTron(mLayerName, mType, mBeta, mGamma, mWordEmb, mPosEmb, mTokEmb); + p->setPluginNamespace(mNamespace.c_str()); + + return p; +} + +DimsExprs EmbLayerNormVarSeqlenPluginHFace::getOutputDimensions( + int32_t outputIndex, const DimsExprs* inputs, int32_t nbInputs, IExprBuilder& exprBuilder) noexcept { // Input should be input ids and token ids and cumulative seqlens // Output should be the embeddings tensor and mask indices @@ -155,7 +192,7 @@ DimsExprs EmbLayerNormVarSeqlenPlugin::getOutputDimensions( // This is a hack: we just report some mask size and rely the plugins to play nicely together. // At runtime, depending on the actual maxSeqlen, the size might be different. - int maskSize_ = packedMaskSize384; + int32_t maskSize_ = packedMaskSize384; auto maskSize = exprBuilder.constant(maskSize_); auto fp16maskSize = exprBuilder.operation(DimensionOperation::kPROD, *maskSize, *exprBuilder.constant(2)); @@ -171,8 +208,31 @@ DimsExprs EmbLayerNormVarSeqlenPlugin::getOutputDimensions( return ret; } -bool EmbLayerNormVarSeqlenPlugin::supportsFormatCombination( - int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) +DimsExprs EmbLayerNormVarSeqlenPluginMTron::getOutputDimensions( + int32_t outputIndex, const DimsExprs* inputs, int32_t nbInputs, IExprBuilder& exprBuilder) noexcept +{ + // Input should be input ids and token ids and cumulative seqlens + // Output should be the embeddings tensor and mask indices + ASSERT(nbInputs == 4); + + ASSERT(inputs[0].nbDims == 1); // sum of all s + ASSERT(inputs[0].nbDims == inputs[1].nbDims); + + ASSERT(inputs[2].nbDims == 1); // B+1 + + ASSERT(outputIndex == 0 || outputIndex == 1); + + DimsExprs ret; + ret.nbDims = 4; + ret.d[0] = inputs[0].d[0]; + ret.d[1] = exprBuilder.constant(mLd); + ret.d[2] = exprBuilder.constant(1); + ret.d[3] = exprBuilder.constant(1); + return ret; +} + +bool EmbLayerNormVarSeqlenPluginBase::supportsFormatCombination( + int32_t pos, const PluginTensorDesc* inOut, int32_t nbInputs, int32_t nbOutputs) noexcept { // The four inputs to this plugin input_ids, segment_ids, cu_seqlens and a dummy input with the // size of the max seq length in that order @@ -211,11 +271,9 @@ bool EmbLayerNormVarSeqlenPlugin::supportsFormatCombination( return desc.type == DataType::kHALF; } -void EmbLayerNormVarSeqlenPlugin::configurePlugin( - const DynamicPluginTensorDesc* inputs, int nbInputs, const DynamicPluginTensorDesc* outputs, int nbOutputs) +void checkConfigurationInputs(const DynamicPluginTensorDesc* inputs, int32_t nbInputs, + const DynamicPluginTensorDesc* outputs, int32_t nbOutputs) noexcept { - gLogVerbose << "EmbLayerNormVarSeqlenPlugin configurePlugin\n"; - // Validate input arguments ASSERT(nbInputs == 4); ASSERT(nbOutputs == 2); @@ -229,11 +287,22 @@ void EmbLayerNormVarSeqlenPlugin::configurePlugin( ASSERT(outputs[0].desc.dims.nbDims == 4); ASSERT(static_cast(outputs[0].desc.dims.d[0]) == static_cast(inputs[0].desc.dims.d[0])); - ASSERT(static_cast(outputs[0].desc.dims.d[1]) == static_cast(mLd)); ASSERT(outputs[0].desc.dims.d[2] == 1); ASSERT(outputs[0].desc.dims.d[3] == 1); - const int B = inputs[2].desc.dims.d[0] - 1; + ASSERT(inputs[0].desc.type == DataType::kINT32); + ASSERT(inputs[1].desc.type == DataType::kINT32); + ASSERT(inputs[2].desc.type == DataType::kINT32); +} + +void EmbLayerNormVarSeqlenPluginHFace::configurePlugin(const DynamicPluginTensorDesc* inputs, int32_t nbInputs, + const DynamicPluginTensorDesc* outputs, int32_t nbOutputs) noexcept +{ + gLogVerbose << "EmbLayerNormVarSeqlenPluginHFace configurePlugin\n"; + checkConfigurationInputs(inputs, nbInputs, outputs, nbOutputs); + ASSERT(static_cast(outputs[0].desc.dims.d[1]) == static_cast(mLd)); + + const int32_t B = inputs[2].desc.dims.d[0] - 1; // check mask ASSERT(outputs[1].desc.dims.nbDims == 2); @@ -244,84 +313,176 @@ void EmbLayerNormVarSeqlenPlugin::configurePlugin( ASSERT((outputs[1].desc.dims.d[1] == 2 * packedMaskSize384) || (outputs[1].desc.dims.d[1] == 2 * packedMaskSize128) || (outputs[1].desc.dims.d[1] == 2 * packedMaskSize256)); - ASSERT(inputs[0].desc.type == DataType::kINT32); - ASSERT(inputs[1].desc.type == DataType::kINT32); - ASSERT(inputs[2].desc.type == DataType::kINT32); ASSERT(outputs[0].desc.type == mType); ASSERT(outputs[1].desc.type == DataType::kHALF); } -size_t EmbLayerNormVarSeqlenPlugin::getWorkspaceSize( - const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const +void EmbLayerNormVarSeqlenPluginMTron::configurePlugin(const DynamicPluginTensorDesc* inputs, int32_t nbInputs, + const DynamicPluginTensorDesc* outputs, int32_t nbOutputs) noexcept +{ + gLogVerbose << "EmbLayerNormVarSeqlenPluginMTron configurePlugin\n"; + checkConfigurationInputs(inputs, nbInputs, outputs, nbOutputs); + ASSERT(static_cast(outputs[0].desc.dims.d[1]) == static_cast(mLd)); + + ASSERT(outputs[1].desc.dims.nbDims == 4); + ASSERT(static_cast(outputs[1].desc.dims.d[0]) == static_cast(inputs[0].desc.dims.d[0])); + ASSERT(static_cast(outputs[1].desc.dims.d[1]) == static_cast(mLd)); + ASSERT(outputs[1].desc.dims.d[2] == 1); + ASSERT(outputs[1].desc.dims.d[3] == 1); + + ASSERT(outputs[0].desc.type == mType); + ASSERT(outputs[1].desc.type == mType); +} + +size_t EmbLayerNormVarSeqlenPluginBase::getWorkspaceSize( + const PluginTensorDesc* inputs, int32_t nbInputs, const PluginTensorDesc* outputs, int32_t nbOutputs) const noexcept { return 0; } -int EmbLayerNormVarSeqlenPlugin::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) +int32_t EmbLayerNormVarSeqlenPluginHFace::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { - int status = -1; - const int batchSize = inputDesc[2].dims.d[0] - 1; - // read out the maximum sequence length from the dummy input - const int maxSeqlen = inputDesc[3].dims.d[0]; - - // There are four versions of the kernel which are optimized for sequence lengths 384, 256, 192 and 128. - // Find the closest sequence length bigger than the max seq length in this batch. - int S = 384; - if (maxSeqlen <= 128) + try { - S = 128; + const int32_t batchSize = inputDesc[2].dims.d[0] - 1; + // read out the maximum sequence length from the dummy input + const int32_t maxSeqlen = inputDesc[3].dims.d[0]; + + // There are four versions of the kernel which are optimized for sequence lengths 384, 256, 192 and 128. + // Find the closest sequence length bigger than the max seq length in this batch. + int32_t S = 384; + if (maxSeqlen <= 128) + { + S = 128; + } + else if (maxSeqlen <= 192) + { + S = 192; + } + else if (maxSeqlen <= 256) + { + S = 256; + } + + // Our plugin outputs only one tensor + const auto inputIds = static_cast(inputs[0]); + const auto segmentIds = static_cast(inputs[1]); + const int32_t* cuSeqlens = static_cast(inputs[2]); + + const float* beta = mBetaDev.get(); + const float* gamma = mGammaDev.get(); + if (mType == DataType::kFLOAT) + { + auto output = static_cast(outputs[0]); + const auto wordEmb = static_cast(mWordEmbDev.get()); + const auto tokEmb = static_cast(mTokEmbDev.get()); + const auto posEmb = static_cast(mPosEmbDev.get()); + + return embSkipLayerNormHFace(stream, static_cast(mLd), batchSize, S, inputIds, segmentIds, + cuSeqlens, beta, gamma, wordEmb, posEmb, tokEmb, output); + } + else if (mType == DataType::kHALF) + { + auto output = static_cast(outputs[0]); + const auto wordEmb = static_cast(mWordEmbDev.get()); + const auto tokEmb = static_cast(mTokEmbDev.get()); + const auto posEmb = static_cast(mPosEmbDev.get()); + + return embSkipLayerNormHFace(stream, static_cast(mLd), batchSize, S, inputIds, segmentIds, + cuSeqlens, beta, gamma, wordEmb, posEmb, tokEmb, output); + } + else + { + gLogError << "Unsupported type error, expected [kHALF,kFLOAT], but received " << static_cast(mType) + << std::endl; + + return STATUS_NOT_SUPPORTED; + } + + return STATUS_SUCCESS; } - else if (maxSeqlen <= 192) + catch (const std::exception& e) { - S = 192; + caughtError(e); } - else if (maxSeqlen <= 256) + return STATUS_FAILURE; +} + +int32_t EmbLayerNormVarSeqlenPluginMTron::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept +{ + try { - S = 256; + const int32_t batchSize = inputDesc[2].dims.d[0] - 1; + // read out the maximum sequence length from the dummy input + const int32_t maxSeqlen = inputDesc[3].dims.d[0]; + + // There are four versions of the kernel which are optimized for sequence lengths 384, 256, 192 and 128. + // Find the closest sequence length bigger than the max seq length in this batch. + int32_t S = 384; + if (maxSeqlen <= 128) + { + S = 128; + } + else if (maxSeqlen <= 192) + { + S = 192; + } + else if (maxSeqlen <= 256) + { + S = 256; + } + + // Our plugin outputs only one tensor + const auto inputIds = static_cast(inputs[0]); + const auto segmentIds = static_cast(inputs[1]); + const int32_t* cuSeqlens = static_cast(inputs[2]); + + const float* beta = mBetaDev.get(); + const float* gamma = mGammaDev.get(); + if (mType == DataType::kFLOAT) + { + auto output = static_cast(outputs[0]); + auto skip = static_cast(outputs[1]); + const auto wordEmb = static_cast(mWordEmbDev.get()); + const auto tokEmb = static_cast(mTokEmbDev.get()); + const auto posEmb = static_cast(mPosEmbDev.get()); + + return embSkipLayerNormMTron(stream, static_cast(mLd), batchSize, S, inputIds, segmentIds, + cuSeqlens, beta, gamma, wordEmb, posEmb, tokEmb, output, skip); + } + else if (mType == DataType::kHALF) + { + auto output = static_cast(outputs[0]); + auto skip = static_cast(outputs[1]); + const auto wordEmb = static_cast(mWordEmbDev.get()); + const auto tokEmb = static_cast(mTokEmbDev.get()); + const auto posEmb = static_cast(mPosEmbDev.get()); + + return embSkipLayerNormMTron(stream, static_cast(mLd), batchSize, S, inputIds, segmentIds, + cuSeqlens, beta, gamma, wordEmb, posEmb, tokEmb, output, skip); + } + else + { + gLogError << "Unsupported type error, expected [kHALF,kFLOAT], but received " << static_cast(mType) + << std::endl; + + return STATUS_NOT_SUPPORTED; + } + + return STATUS_SUCCESS; } - - // Our plugin outputs only one tensor - const auto inputIds = static_cast(inputs[0]); - const auto segmentIds = static_cast(inputs[1]); - const int* cuSeqlens = static_cast(inputs[2]); - - const float* beta = mBetaDev.get(); - const float* gamma = mGammaDev.get(); - if (mType == DataType::kFLOAT) + catch (const std::exception& e) { - auto output = static_cast(outputs[0]); - const auto wordEmb = static_cast(mWordEmbDev.get()); - const auto tokEmb = static_cast(mTokEmbDev.get()); - const auto posEmb = static_cast(mPosEmbDev.get()); - - embSkipLayerNorm2(stream, static_cast(mLd), batchSize, S, inputIds, segmentIds, cuSeqlens, beta, - gamma, wordEmb, posEmb, tokEmb, output); + caughtError(e); } - else if (mType == DataType::kHALF) - { - auto output = static_cast(outputs[0]); - const auto wordEmb = static_cast(mWordEmbDev.get()); - const auto tokEmb = static_cast(mTokEmbDev.get()); - const auto posEmb = static_cast(mPosEmbDev.get()); - - embSkipLayerNorm2(stream, static_cast(mLd), batchSize, S, inputIds, segmentIds, cuSeqlens, beta, - gamma, wordEmb, posEmb, tokEmb, output); - } - else - { - gLogError << "Unsupported type error, expected [kHALF,kFLOAT], but received " << static_cast(mType) - << std::endl; - ASSERT(false); - } - - CHECK(cudaPeekAtLastError()); - - return status; + return STATUS_FAILURE; } // IPluginV2Ext Methods -DataType EmbLayerNormVarSeqlenPlugin::getOutputDataType(int index, const DataType* inputTypes, int nbInputs) const +DataType EmbLayerNormVarSeqlenPluginBase::getOutputDataType( + int32_t index, const DataType* inputTypes, int32_t nbInputs) const noexcept { ASSERT(index == 0 || index == 1); @@ -334,32 +495,49 @@ DataType EmbLayerNormVarSeqlenPlugin::getOutputDataType(int index, const DataTyp } // IPluginV2 Methods -const char* EmbLayerNormVarSeqlenPlugin::getPluginType() const +const char* EmbLayerNormVarSeqlenPluginBase::getPluginType() const noexcept { return EMB_LAYER_NORM_VAR_SEQLEN_NAME; } -const char* EmbLayerNormVarSeqlenPlugin::getPluginVersion() const +const char* EmbLayerNormVarSeqlenPluginHFace::getPluginVersion() const noexcept { - return EMB_LAYER_NORM_VAR_SEQLEN_VERSION; + return EMB_LAYER_NORM_VAR_SEQLEN_VERSION_HFACE; } -int EmbLayerNormVarSeqlenPlugin::getNbOutputs() const +const char* EmbLayerNormVarSeqlenPluginMTron::getPluginVersion() const noexcept +{ + return EMB_LAYER_NORM_VAR_SEQLEN_VERSION_MTRON; +} + +int32_t EmbLayerNormVarSeqlenPluginBase::getNbOutputs() const noexcept { return 2; } -int EmbLayerNormVarSeqlenPlugin::initialize() +int32_t EmbLayerNormVarSeqlenPluginHFace::initialize() noexcept { + gLogVerbose << "EmbLayerNormVarSeqlenPluginHFace initialize\n"; return 0; } -void EmbLayerNormVarSeqlenPlugin::terminate() +int32_t EmbLayerNormVarSeqlenPluginMTron::initialize() noexcept { - gLogVerbose << "EmbLayerNormVarSeqlenPlugin terminate\n"; + gLogVerbose << "EmbLayerNormVarSeqlenPluginMTron initialize\n"; + return 0; } -size_t EmbLayerNormVarSeqlenPlugin::getSerializationSize() const +void EmbLayerNormVarSeqlenPluginHFace::terminate() noexcept +{ + gLogVerbose << "EmbLayerNormVarSeqlenPluginHFace terminate\n"; +} + +void EmbLayerNormVarSeqlenPluginMTron::terminate() noexcept +{ + gLogVerbose << "EmbLayerNormVarSeqlenPluginMTron terminate\n"; +} + +size_t EmbLayerNormVarSeqlenPluginBase::getSerializationSize() const noexcept { const size_t wordSize = getElementSize(mType); return 2 * sizeof(float) * mLd // beta + gamma @@ -374,7 +552,7 @@ size_t EmbLayerNormVarSeqlenPlugin::getSerializationSize() const ; } -void EmbLayerNormVarSeqlenPlugin::serialize(void* buffer) const +void EmbLayerNormVarSeqlenPluginBase::serialize(void* buffer) const noexcept { serialize_value(&buffer, mType); serialize_value(&buffer, mLd); @@ -392,62 +570,79 @@ void EmbLayerNormVarSeqlenPlugin::serialize(void* buffer) const serFromDev(d, static_cast(mTokEmbDev.get()), mLd * mTokVocabSize * wordSize); } -void EmbLayerNormVarSeqlenPlugin::destroy() +void EmbLayerNormVarSeqlenPluginBase::destroy() noexcept { - gLogVerbose << "EmbLayerNormVarSeqlenPlugin destroy\n"; // This gets called when the network containing plugin is destroyed - mGammaDev.release(); - mBetaDev.release(); - mWordEmbDev.release(); - mPosEmbDev.release(); - mTokEmbDev.release(); + mGammaDev.reset(nullptr); + mBetaDev.reset(nullptr); + mWordEmbDev.reset(nullptr); + mPosEmbDev.reset(nullptr); + mTokEmbDev.reset(nullptr); delete this; } -void EmbLayerNormVarSeqlenPlugin::setPluginNamespace(const char* libNamespace) +void EmbLayerNormVarSeqlenPluginHFace::destroy() noexcept { - mNamespace = libNamespace; + gLogVerbose << "EmbLayerNormVarSeqlenPluginHFace destroy\n"; + EmbLayerNormVarSeqlenPluginBase::destroy(); } -const char* EmbLayerNormVarSeqlenPlugin::getPluginNamespace() const +void EmbLayerNormVarSeqlenPluginMTron::destroy() noexcept +{ + gLogVerbose << "EmbLayerNormVarSeqlenPluginMTron destroy\n"; + EmbLayerNormVarSeqlenPluginBase::destroy(); +} + +void EmbLayerNormVarSeqlenPluginBase::setPluginNamespace(const char* libNamespace) noexcept +{ + try + { + mNamespace = libNamespace; + } + catch (const std::exception& e) + { + caughtError(e); + } +} + +const char* EmbLayerNormVarSeqlenPluginBase::getPluginNamespace() const noexcept { return mNamespace.c_str(); } /////////////////////// -EmbLayerNormVarSeqlenPluginCreator::EmbLayerNormVarSeqlenPluginCreator() +EmbLayerNormVarSeqlenPluginBaseCreator::EmbLayerNormVarSeqlenPluginBaseCreator() { mFC.nbFields = mPluginAttributes.size(); mFC.fields = mPluginAttributes.data(); } -const char* EmbLayerNormVarSeqlenPluginCreator::getPluginName() const +const char* EmbLayerNormVarSeqlenPluginBaseCreator::getPluginName() const noexcept { return EMB_LAYER_NORM_VAR_SEQLEN_NAME; } -const char* EmbLayerNormVarSeqlenPluginCreator::getPluginVersion() const +const char* EmbLayerNormVarSeqlenPluginHFaceCreator::getPluginVersion() const noexcept { - return EMB_LAYER_NORM_VAR_SEQLEN_VERSION; + return EMB_LAYER_NORM_VAR_SEQLEN_VERSION_HFACE; } -const PluginFieldCollection* EmbLayerNormVarSeqlenPluginCreator::getFieldNames() +const char* EmbLayerNormVarSeqlenPluginMTronCreator::getPluginVersion() const noexcept +{ + return EMB_LAYER_NORM_VAR_SEQLEN_VERSION_MTRON; +} + +const PluginFieldCollection* EmbLayerNormVarSeqlenPluginBaseCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2* EmbLayerNormVarSeqlenPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +bool initializeFields(const char* name, const PluginFieldCollection* fc, Weights& beta, Weights& gamma, + Weights& word_emb, Weights& pos_emb, Weights& tok_emb) { - gLogVerbose << "EmbLayerNormVarSeqlen createPlugin\n"; - bool output_fp16 = false; - Weights beta; - Weights gamma; - Weights word_emb; - Weights pos_emb; - Weights tok_emb; - for (int i = 0; i < fc->nbFields; i++) + for (int32_t i = 0; i < fc->nbFields; i++) { std::string field_name(fc->fields[i].name); if (field_name.compare("bert_embeddings_layernorm_beta") == 0) @@ -493,30 +688,107 @@ IPluginV2* EmbLayerNormVarSeqlenPluginCreator::createPlugin(const char* name, co { gLogVerbose << "Building output_fp16...\n"; ASSERT(fc->fields[i].type == PluginFieldType::kINT32); - output_fp16 = static_cast(fc->fields[i].data)[0] != 0; + output_fp16 = static_cast(fc->fields[i].data)[0] != 0; } } - - gLogVerbose << "Building the Plugin...\n"; - EmbLayerNormVarSeqlenPlugin* p = new EmbLayerNormVarSeqlenPlugin( - name, output_fp16 ? DataType::kHALF : DataType::kFLOAT, beta, gamma, word_emb, pos_emb, tok_emb); - return p; + return output_fp16; } -IPluginV2* EmbLayerNormVarSeqlenPluginCreator::deserializePlugin( - const char* name, const void* serialData, size_t serialLength) +IPluginV2* EmbLayerNormVarSeqlenPluginHFaceCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { - // This object will be deleted when the network is destroyed, which will - // call EmbLayerNormVarSeqlen::destroy() - return new EmbLayerNormVarSeqlenPlugin(name, serialData, serialLength); + try + { + gLogVerbose << "EmbLayerNormVarSeqlenHFace createPlugin\n"; + + Weights beta; + Weights gamma; + Weights word_emb; + Weights pos_emb; + Weights tok_emb; + bool output_fp16 = initializeFields(name, fc, beta, gamma, word_emb, pos_emb, tok_emb); + + gLogVerbose << "Building the Plugin...\n"; + EmbLayerNormVarSeqlenPluginHFace* p = new EmbLayerNormVarSeqlenPluginHFace( + name, output_fp16 ? DataType::kHALF : DataType::kFLOAT, beta, gamma, word_emb, pos_emb, tok_emb); + return p; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } -void EmbLayerNormVarSeqlenPluginCreator::setPluginNamespace(const char* libNamespace) +IPluginV2* EmbLayerNormVarSeqlenPluginMTronCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { - mNamespace = libNamespace; + try + { + gLogVerbose << "EmbLayerNormVarSeqlenMTron createPlugin\n"; + + Weights beta; + Weights gamma; + Weights word_emb; + Weights pos_emb; + Weights tok_emb; + bool output_fp16 = initializeFields(name, fc, beta, gamma, word_emb, pos_emb, tok_emb); + + gLogVerbose << "Building the Plugin...\n"; + EmbLayerNormVarSeqlenPluginMTron* p = new EmbLayerNormVarSeqlenPluginMTron( + name, output_fp16 ? DataType::kHALF : DataType::kFLOAT, beta, gamma, word_emb, pos_emb, tok_emb); + return p; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } -const char* EmbLayerNormVarSeqlenPluginCreator::getPluginNamespace() const +IPluginV2* EmbLayerNormVarSeqlenPluginHFaceCreator::deserializePlugin( + const char* name, const void* serialData, size_t serialLength) noexcept +{ + try + { + // This object will be deleted when the network is destroyed, which will + // call EmbLayerNormVarSeqlen::destroy() + return new EmbLayerNormVarSeqlenPluginHFace(name, serialData, serialLength); + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; +} + +IPluginV2* EmbLayerNormVarSeqlenPluginMTronCreator::deserializePlugin( + const char* name, const void* serialData, size_t serialLength) noexcept +{ + try + { + // This object will be deleted when the network is destroyed, which will + // call EmbLayerNormVarSeqlen::destroy() + return new EmbLayerNormVarSeqlenPluginMTron(name, serialData, serialLength); + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; +} + +void EmbLayerNormVarSeqlenPluginBaseCreator::setPluginNamespace(const char* libNamespace) noexcept +{ + try + { + mNamespace = libNamespace; + } + catch (const std::exception& e) + { + caughtError(e); + } +} + +const char* EmbLayerNormVarSeqlenPluginBaseCreator::getPluginNamespace() const noexcept { return mNamespace.c_str(); } diff --git a/plugin/embLayerNormPlugin/embLayerNormVarSeqlenPlugin.h b/plugin/embLayerNormPlugin/embLayerNormVarSeqlenPlugin.h old mode 100755 new mode 100644 index 8948975c..a4268c75 --- a/plugin/embLayerNormPlugin/embLayerNormVarSeqlenPlugin.h +++ b/plugin/embLayerNormPlugin/embLayerNormVarSeqlenPlugin.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_EMB_LAYER_NORM_PLUGIN_H #define TRT_EMB_LAYER_NORM_PLUGIN_H @@ -29,59 +28,61 @@ namespace bert { template -int embSkipLayerNormVarSeqlen(cudaStream_t stream, int ld, int B, int S, const uint32_t* cuSeqlens, const int* inputIds, - const int* token_ids, const T* beta, const T* gamma, const T* wordEmb, const T* posEmb, const T* tokEmb, T* output); +int32_t embSkipLayerNormVarSeqlenHFace(cudaStream_t stream, int32_t ld, int32_t B, int32_t S, const uint32_t* cuSeqlens, + const int32_t* inputIds, const int32_t* token_ids, const T* beta, const T* gamma, const T* wordEmb, const T* posEmb, + const T* tokEmb, T* output); template -int embSkipLayerNorm2(cudaStream_t stream, int ld, int B, int S, const int* inputIds, const int* tokenIds, - const int* cuSeqlens, const float* beta, const float* gamma, const T* wordEmb, const T* posEmb, const T* tokEmb, - T* output); +int32_t embSkipLayerNormHFace(cudaStream_t stream, int32_t ld, int32_t B, int32_t S, const int32_t* inputIds, + const int32_t* tokenIds, const int32_t* cuSeqlens, const float* beta, const float* gamma, const T* wordEmb, + const T* posEmb, const T* tokEmb, T* output); + +template +int32_t embSkipLayerNormVarSeqlenMTron(cudaStream_t stream, int32_t ld, int32_t B, int32_t S, const uint32_t* cuSeqlens, + const int32_t* inputIds, const int32_t* token_ids, const T* beta, const T* gamma, const T* wordEmb, const T* posEmb, + const T* tokEmb, T* output, T* skip); + +template +int32_t embSkipLayerNormMTron(cudaStream_t stream, int32_t ld, int32_t B, int32_t S, const int32_t* inputIds, + const int32_t* tokenIds, const int32_t* cuSeqlens, const float* beta, const float* gamma, const T* wordEmb, + const T* posEmb, const T* tokEmb, T* output, T* skip); void cuSeqlensToPackedMask(const uint32_t S, const uint32_t B, const uint32_t warps_m, const uint32_t warps_n, - const uint32_t warps_k, const int* cuSeqlens, uint32_t* inputMaskX, cudaStream_t stream); + const uint32_t warps_k, const int32_t* cuSeqlens, uint32_t* inputMaskX, cudaStream_t stream); -class EmbLayerNormVarSeqlenPlugin : public nvinfer1::IPluginV2DynamicExt +class EmbLayerNormVarSeqlenPluginBase : public nvinfer1::IPluginV2DynamicExt { public: - EmbLayerNormVarSeqlenPlugin(const std::string& name, const nvinfer1::DataType type, const nvinfer1::Weights& beta, - const nvinfer1::Weights& gamma, const nvinfer1::Weights& word_emb, const nvinfer1::Weights& pos_emb, - const nvinfer1::Weights& tok_emb); + EmbLayerNormVarSeqlenPluginBase(const std::string& name, const nvinfer1::DataType type, + const nvinfer1::Weights& beta, const nvinfer1::Weights& gamma, const nvinfer1::Weights& word_emb, + const nvinfer1::Weights& pos_emb, const nvinfer1::Weights& tok_emb); - EmbLayerNormVarSeqlenPlugin(const std::string& name, const void* data, size_t length); + EmbLayerNormVarSeqlenPluginBase(const std::string& name, const void* data, size_t length); // It doesn't make sense to make EmbLayerNormVarSeqlenPlugin without arguments, so we // delete default constructor. - EmbLayerNormVarSeqlenPlugin() = delete; + EmbLayerNormVarSeqlenPluginBase() = delete; // IPluginV2DynamicExt Methods - nvinfer1::IPluginV2DynamicExt* clone() const override; - nvinfer1::DimsExprs getOutputDimensions( - int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) override; bool supportsFormatCombination( - int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) override; - void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, - const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) override; - size_t getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int nbInputs, - const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const override; - int enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) override; + int32_t pos, const nvinfer1::PluginTensorDesc* inOut, int32_t nbInputs, int32_t nbOutputs) noexcept override; + size_t getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int32_t nbInputs, + const nvinfer1::PluginTensorDesc* outputs, int32_t nbOutputs) const noexcept override; // IPluginV2Ext Methods - nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + nvinfer1::DataType getOutputDataType(int32_t index, const nvinfer1::DataType* inputTypes, int32_t nbInputs) const + noexcept override; // IPluginV2 Methods - const char* getPluginType() const override; - const char* getPluginVersion() const override; - int getNbOutputs() const override; - int initialize() override; - void terminate() override; - size_t getSerializationSize() const override; - void serialize(void* buffer) const override; - void destroy() override; - void setPluginNamespace(const char* pluginNamespace) override; - const char* getPluginNamespace() const override; + const char* getPluginType() const noexcept override; + int32_t getNbOutputs() const noexcept override; + size_t getSerializationSize() const noexcept override; + void serialize(void* buffer) const noexcept override; + void destroy() noexcept override; + const char* getPluginNamespace() const noexcept override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; -private: +protected: const std::string mLayerName; std::string mNamespace; @@ -100,41 +101,102 @@ private: bert::WeightsWithOwnership mTokEmb; bert::WeightsWithOwnership mPosEmb; nvinfer1::DataType mType; - -protected: - // To prevent compiler warnings. - using nvinfer1::IPluginV2DynamicExt::canBroadcastInputAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::configurePlugin; - using nvinfer1::IPluginV2DynamicExt::enqueue; - using nvinfer1::IPluginV2DynamicExt::getOutputDimensions; - using nvinfer1::IPluginV2DynamicExt::getWorkspaceSize; - using nvinfer1::IPluginV2DynamicExt::isOutputBroadcastAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::supportsFormat; }; -class EmbLayerNormVarSeqlenPluginCreator : public nvinfer1::IPluginCreator +class EmbLayerNormVarSeqlenPluginHFace : public EmbLayerNormVarSeqlenPluginBase { public: - EmbLayerNormVarSeqlenPluginCreator(); + EmbLayerNormVarSeqlenPluginHFace(const std::string& name, const nvinfer1::DataType type, const nvinfer1::Weights& beta, + const nvinfer1::Weights& gamma, const nvinfer1::Weights& word_emb, const nvinfer1::Weights& pos_emb, + const nvinfer1::Weights& tok_emb); - const char* getPluginName() const override; + EmbLayerNormVarSeqlenPluginHFace(const std::string& name, const void* data, size_t length); - const char* getPluginVersion() const override; + // It doesn't make sense to make EmbLayerNormVarSeqlenPlugin without arguments, so we + // delete default constructor. + EmbLayerNormVarSeqlenPluginHFace() = delete; - const nvinfer1::PluginFieldCollection* getFieldNames() override; + // IPluginV2DynamicExt Methods + nvinfer1::IPluginV2DynamicExt* clone() const noexcept override; + nvinfer1::DimsExprs getOutputDimensions(int32_t outputIndex, const nvinfer1::DimsExprs* inputs, int32_t nbInputs, + nvinfer1::IExprBuilder& exprBuilder) noexcept override; + void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int32_t nbInputs, + const nvinfer1::DynamicPluginTensorDesc* out, int32_t nbOutputs) noexcept override; + int32_t enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; - nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) override; + // IPluginV2 Methods + int32_t initialize() noexcept override; + void terminate() noexcept override; + void destroy() noexcept override; + const char* getPluginVersion() const noexcept override; +}; - nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; +class EmbLayerNormVarSeqlenPluginMTron : public EmbLayerNormVarSeqlenPluginBase +{ +public: + EmbLayerNormVarSeqlenPluginMTron(const std::string& name, const nvinfer1::DataType type, const nvinfer1::Weights& beta, + const nvinfer1::Weights& gamma, const nvinfer1::Weights& word_emb, const nvinfer1::Weights& pos_emb, + const nvinfer1::Weights& tok_emb); - void setPluginNamespace(const char* pluginNamespace) override; + EmbLayerNormVarSeqlenPluginMTron(const std::string& name, const void* data, size_t length); - const char* getPluginNamespace() const override; + // It doesn't make sense to make EmbLayerNormVarSeqlenPlugin without arguments, so we + // delete default constructor. + EmbLayerNormVarSeqlenPluginMTron() = delete; -private: + // IPluginV2DynamicExt Methods + nvinfer1::IPluginV2DynamicExt* clone() const noexcept override; + nvinfer1::DimsExprs getOutputDimensions(int32_t outputIndex, const nvinfer1::DimsExprs* inputs, int32_t nbInputs, + nvinfer1::IExprBuilder& exprBuilder) noexcept override; + void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int32_t nbInputs, + const nvinfer1::DynamicPluginTensorDesc* out, int32_t nbOutputs) noexcept override; + int32_t enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; + + // IPluginV2 Methods + int32_t initialize() noexcept override; + void terminate() noexcept override; + void destroy() noexcept override; + const char* getPluginVersion() const noexcept override; +}; + +class EmbLayerNormVarSeqlenPluginBaseCreator : public nvinfer1::IPluginCreator +{ +public: + EmbLayerNormVarSeqlenPluginBaseCreator(); + + const char* getPluginName() const noexcept override; + + const nvinfer1::PluginFieldCollection* getFieldNames() noexcept override; + + void setPluginNamespace(const char* pluginNamespace) noexcept override; + + const char* getPluginNamespace() const noexcept override; + +protected: static nvinfer1::PluginFieldCollection mFC; static std::vector mPluginAttributes; std::string mNamespace; }; + +class EmbLayerNormVarSeqlenPluginHFaceCreator : public EmbLayerNormVarSeqlenPluginBaseCreator +{ +public: + nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) noexcept override; + const char* getPluginVersion() const noexcept override; + nvinfer1::IPluginV2* deserializePlugin( + const char* name, const void* serialData, size_t serialLength) noexcept override; +}; + +class EmbLayerNormVarSeqlenPluginMTronCreator : public EmbLayerNormVarSeqlenPluginBaseCreator +{ +public: + nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) noexcept override; + const char* getPluginVersion() const noexcept override; + nvinfer1::IPluginV2* deserializePlugin( + const char* name, const void* serialData, size_t serialLength) noexcept override; +}; + } // namespace bert #endif // TRT_EMB_LAYER_NORM_PLUGIN_H diff --git a/plugin/exports.map b/plugin/exports.map index 08e13688..80d80d5b 100644 --- a/plugin/exports.map +++ b/plugin/exports.map @@ -21,7 +21,11 @@ getPluginRegistry; initLibNvInferPlugins; extern "C++" { - nvinfer1::*; + nvinfer1::IPluginCreator::*; + nvinfer1::IPluginV2Ext::*; + nvinfer1::IPluginV2IOExt::*; + nvinfer1::PluginRegistrar*; + nvinfer1::plugin::*; }; local: *; }; diff --git a/plugin/fcPlugin/fcPlugin.cpp b/plugin/fcPlugin/fcPlugin.cpp index abaf72b4..b47736a2 100644 --- a/plugin/fcPlugin/fcPlugin.cpp +++ b/plugin/fcPlugin/fcPlugin.cpp @@ -38,8 +38,8 @@ namespace bert // plugin specific constants namespace { -static const char* FC_VERSION{"1"}; -static const char* FC_NAME{"CustomFCPluginDynamic"}; +const char* FC_VERSION{"1"}; +const char* FC_NAME{"CustomFCPluginDynamic"}; } // namespace // Static class fields initialization @@ -59,11 +59,7 @@ static void printPerfStructure(const customMatmulPerf_t& perf, int const& m, int double timeAvg = (perf.time * 1e-3) / kernelRepeats; // Convert to seconds, then divide by loops double gflop = (2 * static_cast(m * n) * k) * 1e-9; // Real - gLogVerbose << "Algo=" << p.algoId << " Tile=" << p.tile << " (" << matmulTileName[p.tile] << ") K=" << p.numSplitsK - << " Red.Sch.=" << p.reductionScheme << " Swiz=" << p.swizzle << " Cust=" << p.customOption - << " Stat=" << perf.status << " Time=" << perf.time << " WSbytes=" << perf.workspaceSize - << " math=" << p.mathMode << " waves=" << perf.wavesCount << "GFlops=" << (gflop / timeAvg) - << std::endl; + gLogVerbose << "Algo=" << p.algoId << " Tile=" << p.tile << " (" << matmulTileName[p.tile] << ") K=" << p.numSplitsK << " Red.Sch.=" << p.reductionScheme << " Swiz=" << p.swizzle << " Cust=" << p.customOption << " Stat=" << perf.status << " Time=" << perf.time << " WSbytes=" << perf.workspaceSize << " math=" << p.mathMode << " waves=" << perf.wavesCount << "GFlops=" << (gflop / timeAvg) << std::endl; } static inline bool time_compare(const customMatmulPerf_t& perf_a, const customMatmulPerf_t& perf_b) @@ -336,7 +332,7 @@ void LtGemmSearch(cublasLtHandle_t ltHandle, cublasOperation_t transa, cublasOpe // for (int i = 0; i < perfResults.size(); i++){ for (int i = 0; i < printAlgos; i++) { - if (perfResults[i].time == 1000000.f) + if (perfResults[i].time == 1000000.F) break; printPerfStructure(perfResults[i], m, n, k); } @@ -387,44 +383,61 @@ FCPluginDynamic::FCPluginDynamic(const std::string name, const void* data, size_ } // IPluginV2DynamicExt Methods -IPluginV2DynamicExt* FCPluginDynamic::clone() const +IPluginV2DynamicExt* FCPluginDynamic::clone() const noexcept { - gLogVerbose << "FCPluginDynamic clone\n"; + try + { + gLogVerbose << "FCPluginDynamic clone\n"; - auto p = new FCPluginDynamic(mLayerName, mType, mOutDim, mW); - memcpy(p->mAlgo.data, mAlgo.data, sizeof(mAlgo.data)); - p->setPluginNamespace(mNamespace.c_str()); + auto* p = new FCPluginDynamic(mLayerName, mType, mOutDim, mW); + memcpy(p->mAlgo.data, mAlgo.data, sizeof(mAlgo.data)); + p->setPluginNamespace(mNamespace.c_str()); - return p; + return p; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } void FCPluginDynamic::attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, nvinfer1::IGpuAllocator* gpuAllocator) + cudnnContext* cudnnContext, cublasContext* cublasContext, nvinfer1::IGpuAllocator* gpuAllocator) noexcept { mLtContext.attach(); } -void FCPluginDynamic::detachFromContext() +void FCPluginDynamic::detachFromContext() noexcept { mLtContext.detach(); } DimsExprs FCPluginDynamic::getOutputDimensions( - int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) + int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) noexcept { - assert(nbInputs == 1); - assert(outputIndex == 0); - DimsExprs ret; - ret.nbDims = 5; - ret.d[0] = inputs[0].d[0]; - ret.d[1] = inputs[0].d[1]; - ret.d[2] = exprBuilder.constant(mOutDim); - ret.d[3] = exprBuilder.constant(1); - ret.d[4] = exprBuilder.constant(1); - return ret; + try + { + assert(nbInputs == 1); + assert(outputIndex == 0); + DimsExprs ret; + ret.nbDims = 5; + ret.d[0] = inputs[0].d[0]; + ret.d[1] = inputs[0].d[1]; + ret.d[2] = exprBuilder.constant(mOutDim); + ret.d[3] = exprBuilder.constant(1); + ret.d[4] = exprBuilder.constant(1); + return ret; + } + catch (const std::exception& e) + { + caughtError(e); + } + return DimsExprs{}; } -bool FCPluginDynamic::supportsFormatCombination(int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) +bool FCPluginDynamic::supportsFormatCombination( + int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept { assert(nbInputs == 1); assert(nbOutputs == 1); @@ -441,134 +454,154 @@ bool FCPluginDynamic::supportsFormatCombination(int pos, const PluginTensorDesc* } void FCPluginDynamic::configurePlugin( - const DynamicPluginTensorDesc* inputs, int nbInputs, const DynamicPluginTensorDesc* outputs, int nbOutputs) + const DynamicPluginTensorDesc* inputs, int nbInputs, const DynamicPluginTensorDesc* outputs, int nbOutputs) noexcept { - // Validate input arguments - assert(nbOutputs == 1); - assert(nbInputs == 1); - assert(mType == inputs[0].desc.type); - const auto& inDims0 = inputs[0].desc.dims; - - assert(inDims0.nbDims == 5); - mK = inDims0.d[HDIM]; // hiddensize - // assert(hiddenSize * mOutDim == mNumParams); - assert(inDims0.d[3] == 1); - assert(inDims0.d[4] == 1); - - // m and k are mOutDim - // n is B*S - const int S = inputs->max.d[SDIM]; - const int B = inputs->max.d[BDIM]; - - mNmax = S * B; - - if (mType == DataType::kFLOAT) + try { - Gemm g(mOutDim, mNmax, mK, false, false); - mLtContext.create(g, maxWorkspaceBytes); - } - else if (mType == DataType::kHALF) - { - Gemm g(mOutDim, mNmax, mK, false, false); - mLtContext.create(g, maxWorkspaceBytes); - } - else - { - gLogError << "Unsupported type error, expected [kHALF,kFLOAT], but received " << static_cast(mType) - << std::endl; - assert(false); - } + // Validate input arguments + assert(nbOutputs == 1); + assert(nbInputs == 1); + assert(mType == inputs[0].desc.type); + const auto& inDims0 = inputs[0].desc.dims; - gLogVerbose << "FCPluginDynamic configurePlugin m=" << mOutDim << ", n=" << mNmax << ", k=" << mK << std::endl; + assert(inDims0.nbDims == 5); + mK = inDims0.d[HDIM]; // hiddensize + // assert(hiddenSize * mOutDim == mNumParams); + assert(inDims0.d[3] == 1); + assert(inDims0.d[4] == 1); + + // m and k are mOutDim + // n is B*S + const int S = inputs->max.d[SDIM]; + const int B = inputs->max.d[BDIM]; + + mNmax = S * B; + + // Cleanup LtContext descriptors before creating new ones. + mLtContext.destroy(); - size_t actualWorkspace = 0; - if (mAlgo.data[0] == 0 && memcmp(mAlgo.data, mAlgo.data + 1, sizeof(mAlgo.data) - sizeof(mAlgo.data[0])) == 0) - { - gLogVerbose << "FCPluginDynamic gemmSearch\n"; if (mType == DataType::kFLOAT) { - mAlgo = gemmSearch(mOutDim, mNmax, mK, maxWorkspaceBytes, actualWorkspace); + Gemm g(mOutDim, mNmax, mK, false, false); + mLtContext.create(g, maxWorkspaceBytes); } else if (mType == DataType::kHALF) { - mAlgo = gemmSearch(mOutDim, mNmax, mK, maxWorkspaceBytes, actualWorkspace); + Gemm g(mOutDim, mNmax, mK, false, false); + mLtContext.create(g, maxWorkspaceBytes); } + else + { + gLogError << "Unsupported type error, expected [kHALF,kFLOAT], but received " << static_cast(mType) + << std::endl; + assert(false); + } + + gLogVerbose << "FCPluginDynamic configurePlugin m=" << mOutDim << ", n=" << mNmax << ", k=" << mK << std::endl; + + size_t actualWorkspace = 0; + if (mAlgo.data[0] == 0 && memcmp(mAlgo.data, mAlgo.data + 1, sizeof(mAlgo.data) - sizeof(mAlgo.data[0])) == 0) + { + gLogVerbose << "FCPluginDynamic gemmSearch\n"; + if (mType == DataType::kFLOAT) + { + mAlgo = gemmSearch(mOutDim, mNmax, mK, maxWorkspaceBytes, actualWorkspace); + } + else if (mType == DataType::kHALF) + { + mAlgo = gemmSearch(mOutDim, mNmax, mK, maxWorkspaceBytes, actualWorkspace); + } + } + + AlgoProps p; + p.populate(mAlgo); + + if (mType == DataType::kFLOAT && p.mathMode == 1) + { + gLogWarning << "cuBLAS might use mixed precision instead of FP32" << std::endl; + } + + if (mType == DataType::kHALF && p.mathMode == 0) + { + gLogWarning << "TensorCore support was not selected" << std::endl; + } + + gLogVerbose << "FCPluginDynamic configuration Algo=" << p.algoId << " Tile=" << p.tile << " (" + << matmulTileName[p.tile] << ") K=" << p.numSplitsK << " Red.Sch.=" << p.reductionScheme + << " Swiz=" << p.swizzle << " Cust=" << p.customOption << " mathMode=" << p.mathMode + << " ws=" << actualWorkspace << std::endl; } - - AlgoProps p; - p.populate(mAlgo); - - if (mType == DataType::kFLOAT && p.mathMode == 1) + catch (const std::exception& e) { - gLogWarning << "cuBLAS might use mixed precision instead of FP32" << std::endl; + caughtError(e); } - - if (mType == DataType::kHALF && p.mathMode == 0) - { - gLogWarning << "TensorCore support was not selected" << std::endl; - } - - gLogVerbose << "FCPluginDynamic configuration Algo=" << p.algoId << " Tile=" << p.tile << " (" - << matmulTileName[p.tile] << ") K=" << p.numSplitsK << " Red.Sch.=" << p.reductionScheme - << " Swiz=" << p.swizzle << " Cust=" << p.customOption << " mathMode=" << p.mathMode - << " ws=" << actualWorkspace << std::endl; } size_t FCPluginDynamic::getWorkspaceSize( - const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const + const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const noexcept { return maxWorkspaceBytes; } int FCPluginDynamic::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workSpace, cudaStream_t stream) + const void* const* inputs, void* const* outputs, void* workSpace, cudaStream_t stream) noexcept { - const size_t workspaceSize = getWorkspaceSize(inputDesc, 1, outputDesc, 1); - - int status = -1; - - const int S = inputDesc->dims.d[SDIM]; - const int B = inputDesc->dims.d[BDIM]; - const int n = S * B; - mLtContext.setN(n); - - if (mType == DataType::kFLOAT) + try { - const auto input = static_cast(inputs[0]); - auto output = static_cast(outputs[0]); + const size_t workspaceSize = getWorkspaceSize(inputDesc, 1, outputDesc, 1); - Gemm g(mOutDim, n, mK, false, false); - assert(mWdev != nullptr); - g.A = static_cast(mWdev.get()); - g.B = const_cast(input); - g.C = output; + const int S = inputDesc->dims.d[SDIM]; + const int B = inputDesc->dims.d[BDIM]; + const int n = S * B; + mLtContext.setN(n); - CUBLASASSERT(cublasLtMatmul(mLtContext, g, mAlgo, workSpace, workspaceSize, stream)); + if (mType == DataType::kFLOAT) + { + const auto* const input = static_cast(inputs[0]); + auto* output = static_cast(outputs[0]); + + Gemm g(mOutDim, n, mK, false, false); + if (mWdev == nullptr) + { + return STATUS_FAILURE; + } + g.A = static_cast(mWdev.get()); + g.B = const_cast(input); + g.C = output; + + return cublasLtMatmul(mLtContext, g, mAlgo, workSpace, workspaceSize, stream); + } + else if (mType == DataType::kHALF) + { + const auto* const input = static_cast(inputs[0]); + auto* output = static_cast(outputs[0]); + + Gemm g(mOutDim, n, mK, false, false); + if (mWdev == nullptr) + { + return STATUS_FAILURE; + } + g.A = static_cast(mWdev.get()); + g.B = const_cast(input); + g.C = output; + return cublasLtMatmul(mLtContext, g, mAlgo, workSpace, workspaceSize, stream); + } + else + { + gLogError << "Unsupported type error, expected [kHALF,kFLOAT], but received " << static_cast(mType) + << std::endl; + return STATUS_FAILURE; + } } - else if (mType == DataType::kHALF) + catch (const std::exception& e) { - const auto input = static_cast(inputs[0]); - auto output = static_cast(outputs[0]); - - Gemm g(mOutDim, n, mK, false, false); - assert(mWdev != nullptr); - g.A = static_cast(mWdev.get()); - g.B = const_cast(input); - g.C = output; - CUBLASASSERT(cublasLtMatmul(mLtContext, g, mAlgo, workSpace, workspaceSize, stream)); + caughtError(e); } - else - { - gLogError << "Unsupported type error, expected [kHALF,kFLOAT], but received " << static_cast(mType) - << std::endl; - assert(false); - } - - return status; + return -1; } // IPluginV2Ext Methods -DataType FCPluginDynamic::getOutputDataType(int index, const DataType* inputTypes, int nbInputs) const +DataType FCPluginDynamic::getOutputDataType(int index, const DataType* inputTypes, int nbInputs) const noexcept { assert(index == 0); assert(nbInputs == 1); @@ -577,41 +610,40 @@ DataType FCPluginDynamic::getOutputDataType(int index, const DataType* inputType } // IPluginV2 Methods -const char* FCPluginDynamic::getPluginType() const +const char* FCPluginDynamic::getPluginType() const noexcept { return FC_NAME; } -const char* FCPluginDynamic::getPluginVersion() const +const char* FCPluginDynamic::getPluginVersion() const noexcept { return FC_VERSION; } -int FCPluginDynamic::getNbOutputs() const +int FCPluginDynamic::getNbOutputs() const noexcept { return 1; } -int FCPluginDynamic::initialize() +int FCPluginDynamic::initialize() noexcept { gLogVerbose << "FCPluginDynamic initialize\n"; return 0; } -void FCPluginDynamic::terminate() +void FCPluginDynamic::terminate() noexcept { gLogVerbose << "FCPluginDynamic terminate\n"; } -size_t FCPluginDynamic::getSerializationSize() const +size_t FCPluginDynamic::getSerializationSize() const noexcept { - size_t wordSize = getElementSize(mType); return wordSize * mNumParams + sizeof(mType) + sizeof(mOutDim) + sizeof(mNumParams) + sizeof(mAlgo) + sizeof(mNmax) + sizeof(mK); } -void FCPluginDynamic::serialize(void* buffer) const +void FCPluginDynamic::serialize(void* buffer) const noexcept { serialize_value(&buffer, mType); serialize_value(&buffer, mOutDim); @@ -625,21 +657,28 @@ void FCPluginDynamic::serialize(void* buffer) const serFromDev(d, static_cast(mWdev.get()), mNumParams * wordSize); } -void FCPluginDynamic::destroy() +void FCPluginDynamic::destroy() noexcept { gLogVerbose << "FCPluginDynamic destroy\n"; // This gets called when the network containing plugin is destroyed mLtContext.destroy(); - mWdev.release(); + mWdev.reset(nullptr); delete this; } -void FCPluginDynamic::setPluginNamespace(const char* libNamespace) +void FCPluginDynamic::setPluginNamespace(const char* libNamespace) noexcept { - mNamespace = libNamespace; + try + { + mNamespace = libNamespace; + } + catch (const std::exception& e) + { + caughtError(e); + } } -const char* FCPluginDynamic::getPluginNamespace() const +const char* FCPluginDynamic::getPluginNamespace() const noexcept { return mNamespace.c_str(); } @@ -648,90 +687,116 @@ const char* FCPluginDynamic::getPluginNamespace() const FCPluginDynamicCreator::FCPluginDynamicCreator() { + mPluginAttributes.emplace_back(PluginField("out_dims", nullptr, PluginFieldType::kINT32, 1)); + mPluginAttributes.emplace_back(PluginField("type_id", nullptr, PluginFieldType::kINT32, 1)); + mPluginAttributes.emplace_back(PluginField("W", nullptr, PluginFieldType::kFLOAT32, 1)); + mFC.nbFields = mPluginAttributes.size(); mFC.fields = mPluginAttributes.data(); } -const char* FCPluginDynamicCreator::getPluginName() const +const char* FCPluginDynamicCreator::getPluginName() const noexcept { return FC_NAME; } -const char* FCPluginDynamicCreator::getPluginVersion() const +const char* FCPluginDynamicCreator::getPluginVersion() const noexcept { return FC_VERSION; } -const PluginFieldCollection* FCPluginDynamicCreator::getFieldNames() +const PluginFieldCollection* FCPluginDynamicCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2* FCPluginDynamicCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2* FCPluginDynamicCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { - gLogVerbose << "Creating FCPluginDynamicCreator...\n"; - - int outDims = 0; - int typeId = -1; - Weights W; - W.count = 0; - W.values = nullptr; - - for (int i = 0; i < fc->nbFields; i++) + try { - std::string field_name(fc->fields[i].name); - if (field_name.compare("out_dims") == 0) + gLogVerbose << "Creating FCPluginDynamicCreator...\n"; + + int outDims = 0; + int typeId = -1; + Weights W{DataType::kFLOAT, nullptr, 0ll}; + + for (int i = 0; i < fc->nbFields; i++) { - outDims = static_cast(fc->fields[i].data)[0]; - gLogVerbose << "Building outDims: " << outDims << std::endl; + std::string field_name(fc->fields[i].name); + if (field_name.compare("out_dims") == 0) + { + outDims = static_cast(fc->fields[i].data)[0]; + gLogVerbose << "Building outDims: " << outDims << std::endl; + } + + if (field_name.compare("type_id") == 0) + { + typeId = static_cast(fc->fields[i].data)[0]; + gLogVerbose << "Building typeId: " << outDims << std::endl; + } + + if (field_name.compare("W") == 0) + { + gLogVerbose << "Building W...\n"; + W.values = fc->fields[i].data; + W.count = fc->fields[i].length; + W.type = fieldTypeToDataType(fc->fields[i].type); + gLogVerbose << "Is W float32: " << (W.type == DataType::kFLOAT) << std::endl; + } } - if (field_name.compare("type_id") == 0) + if (outDims <= 0) { - typeId = static_cast(fc->fields[i].data)[0]; - gLogVerbose << "Building typeId: " << outDims << std::endl; + gLogError << "Invalid output dimension" << std::endl; + } + if (typeId < 0 || typeId > 3) + { + gLogError << "Invalid type id" << typeId << std::endl; + } + if (W.count == 0 || W.values == nullptr || W.count < outDims) + { + gLogError << "Invalid weights" << std::endl; } - if (field_name.compare("W") == 0) - { - gLogVerbose << "Building W...\n"; - W.values = fc->fields[i].data; - W.count = fc->fields[i].length; - W.type = fieldTypeToDataType(fc->fields[i].type); - gLogVerbose << "Is W float32: " << (W.type == DataType::kFLOAT) << std::endl; - } + DataType type = static_cast(typeId); + return new FCPluginDynamic(name, type, outDims, W); } - - if (outDims <= 0) + catch (const std::exception& e) { - gLogError << "Invalid output dimension" << std::endl; + caughtError(e); } - if (typeId < 0 || typeId > 3) - { - gLogError << "Invalid type id" << typeId << std::endl; - } - if (W.count == 0 || W.values == nullptr || W.count < outDims) - { - gLogError << "Invalid weights" << std::endl; - } - - DataType type = static_cast(typeId); - return new FCPluginDynamic(name, type, outDims, W); + return nullptr; } -IPluginV2* FCPluginDynamicCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) +IPluginV2* FCPluginDynamicCreator::deserializePlugin( + const char* name, const void* serialData, size_t serialLength) noexcept { // This object will be deleted when the network is destroyed, which will // call FCPluginDynamic::destroy() - return new FCPluginDynamic(name, serialData, serialLength); + try + { + return new FCPluginDynamic(name, serialData, serialLength); + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } -void FCPluginDynamicCreator::setPluginNamespace(const char* libNamespace) +void FCPluginDynamicCreator::setPluginNamespace(const char* libNamespace) noexcept { - mNamespace = libNamespace; + try + { + mNamespace = libNamespace; + } + catch (const std::exception& e) + { + caughtError(e); + } } -const char* FCPluginDynamicCreator::getPluginNamespace() const +const char* FCPluginDynamicCreator::getPluginNamespace() const noexcept { return mNamespace.c_str(); } diff --git a/plugin/fcPlugin/fcPlugin.h b/plugin/fcPlugin/fcPlugin.h index 4082f491..98962b96 100644 --- a/plugin/fcPlugin/fcPlugin.h +++ b/plugin/fcPlugin/fcPlugin.h @@ -99,7 +99,7 @@ struct Gemm init(m_, n_, k_, tA, tB); } - void init(int m_, int n_, int k_, bool tA, bool tB) + void init(int m_, int n_, int k_, bool tA, bool tB) noexcept { m = m_; n = n_; @@ -238,18 +238,22 @@ struct LtContext if (operationDesc) { cublasLtMatmulDescDestroy(operationDesc); + operationDesc = nullptr; } if (Adesc) { cublasLtMatrixLayoutDestroy(Adesc); + Adesc = nullptr; } if (Bdesc) { cublasLtMatrixLayoutDestroy(Bdesc); + Bdesc = nullptr; } if (Cdesc) { cublasLtMatrixLayoutDestroy(Cdesc); + Cdesc = nullptr; } } @@ -441,35 +445,36 @@ public: FCPluginDynamic() = delete; // IPluginV2DynamicExt Methods - nvinfer1::IPluginV2DynamicExt* clone() const override; - nvinfer1::DimsExprs getOutputDimensions( - int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) override; + nvinfer1::IPluginV2DynamicExt* clone() const noexcept override; + nvinfer1::DimsExprs getOutputDimensions(int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, + nvinfer1::IExprBuilder& exprBuilder) noexcept override; bool supportsFormatCombination( - int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) override; + int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept override; void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, - const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) override; + const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) noexcept override; size_t getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int nbInputs, - const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const override; + const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const noexcept override; int enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) override; + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; // IPluginV2Ext Methods - nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const + noexcept override; // IPluginV2 Methods - const char* getPluginType() const override; - const char* getPluginVersion() const override; - int getNbOutputs() const override; - int initialize() override; - void terminate() override; - size_t getSerializationSize() const override; - void serialize(void* buffer) const override; - void destroy() override; - void setPluginNamespace(const char* pluginNamespace) override; + const char* getPluginType() const noexcept override; + const char* getPluginVersion() const noexcept override; + int getNbOutputs() const noexcept override; + int initialize() noexcept override; + void terminate() noexcept override; + size_t getSerializationSize() const noexcept override; + void serialize(void* buffer) const noexcept override; + void destroy() noexcept override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; void attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, nvinfer1::IGpuAllocator* gpuAllocator) override; - void detachFromContext() override; - const char* getPluginNamespace() const override; + cudnnContext* cudnnContext, cublasContext* cublasContext, nvinfer1::IGpuAllocator* gpuAllocator) noexcept override; + void detachFromContext() noexcept override; + const char* getPluginNamespace() const noexcept override; private: const std::string mLayerName; @@ -487,16 +492,6 @@ private: bert::cuda_unique_ptr mWdev; LtContext mLtContext; - -protected: - // To prevent compiler warnings. - using nvinfer1::IPluginV2DynamicExt::canBroadcastInputAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::configurePlugin; - using nvinfer1::IPluginV2DynamicExt::enqueue; - using nvinfer1::IPluginV2DynamicExt::getOutputDimensions; - using nvinfer1::IPluginV2DynamicExt::getWorkspaceSize; - using nvinfer1::IPluginV2DynamicExt::isOutputBroadcastAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::supportsFormat; }; class FCPluginDynamicCreator : public nvinfer1::IPluginCreator @@ -504,19 +499,19 @@ class FCPluginDynamicCreator : public nvinfer1::IPluginCreator public: FCPluginDynamicCreator(); - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const nvinfer1::PluginFieldCollection* getFieldNames() override; + const nvinfer1::PluginFieldCollection* getFieldNames() noexcept override; - nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) override; + nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) noexcept override; - nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; private: static nvinfer1::PluginFieldCollection mFC; diff --git a/plugin/flattenConcat/flattenConcat.cpp b/plugin/flattenConcat/flattenConcat.cpp index d3238c1c..13f3a038 100644 --- a/plugin/flattenConcat/flattenConcat.cpp +++ b/plugin/flattenConcat/flattenConcat.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "flattenConcat.h" #include #include @@ -41,8 +40,8 @@ FlattenConcat::FlattenConcat(int concatAxis, bool ignoreBatch) ASSERT(mConcatAxisID == 1 || mConcatAxisID == 2 || mConcatAxisID == 3); } -FlattenConcat::FlattenConcat(int concatAxis, bool ignoreBatch, int numInputs, int outputConcatAxis, - const int* inputConcatAxis, const size_t* copySize) +FlattenConcat::FlattenConcat( + int concatAxis, bool ignoreBatch, int numInputs, int outputConcatAxis, const int* inputConcatAxis, const size_t* copySize) : mCopySize(numInputs) , mInputConcatAxis(numInputs) , mIgnoreBatch(ignoreBatch) @@ -69,7 +68,7 @@ FlattenConcat::FlattenConcat(const void* data, size_t length) mInputConcatAxis.resize(mNumInputs); std::for_each(mInputConcatAxis.begin(), mInputConcatAxis.end(), [&](int& inp) { inp = read(d); }); - mCHW = read(d); + mCHW = read(d); mCopySize.resize(mNumInputs); std::for_each(mCopySize.begin(), mCopySize.end(), [&](size_t& inp) { inp = read(d); }); @@ -77,94 +76,117 @@ FlattenConcat::FlattenConcat(const void* data, size_t length) ASSERT(d == a + length); } -FlattenConcat::~FlattenConcat() {} +FlattenConcat::~FlattenConcat() +{ +} -int FlattenConcat::getNbOutputs() const +int FlattenConcat::getNbOutputs() const noexcept { return 1; } -Dims FlattenConcat::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) +Dims FlattenConcat::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept { - ASSERT(nbInputDims >= 1); - ASSERT(index == 0); - - mNumInputs = nbInputDims; - mCopySize.resize(mNumInputs); - mInputConcatAxis.resize(mNumInputs); - int outputConcatAxis = 0; - - for (int i = 0; i < nbInputDims; ++i) + try { - int flattenInput = 0; - ASSERT(inputs[i].nbDims == 3); - if (mConcatAxisID != 1) - { - ASSERT(inputs[i].d[0] == inputs[0].d[0]); - } - if (mConcatAxisID != 2) - { - ASSERT(inputs[i].d[1] == inputs[0].d[1]); - } - if (mConcatAxisID != 3) - { - ASSERT(inputs[i].d[2] == inputs[0].d[2]); - } - flattenInput = inputs[i].d[0] * inputs[i].d[1] * inputs[i].d[2]; - outputConcatAxis += flattenInput; - } + ASSERT(nbInputDims >= 1); + ASSERT(index == 0); - return DimsCHW(mConcatAxisID == 1 ? outputConcatAxis : 1, mConcatAxisID == 2 ? outputConcatAxis : 1, - mConcatAxisID == 3 ? outputConcatAxis : 1); + mNumInputs = nbInputDims; + mCopySize.resize(mNumInputs); + mInputConcatAxis.resize(mNumInputs); + int outputConcatAxis = 0; + + for (int i = 0; i < nbInputDims; ++i) + { + int flattenInput = 0; + ASSERT(inputs[i].nbDims == 3); + if (mConcatAxisID != 1) + { + ASSERT(inputs[i].d[0] == inputs[0].d[0]); + } + if (mConcatAxisID != 2) + { + ASSERT(inputs[i].d[1] == inputs[0].d[1]); + } + if (mConcatAxisID != 3) + { + ASSERT(inputs[i].d[2] == inputs[0].d[2]); + } + flattenInput = inputs[i].d[0] * inputs[i].d[1] * inputs[i].d[2]; + outputConcatAxis += flattenInput; + } + + return Dims3(mConcatAxisID == 1 ? outputConcatAxis : 1, mConcatAxisID == 2 ? outputConcatAxis : 1, + mConcatAxisID == 3 ? outputConcatAxis : 1); + } + catch (const std::exception& e) + { + caughtError(e); + } + return Dims{}; } -int FlattenConcat::initialize() +int FlattenConcat::initialize() noexcept { return STATUS_SUCCESS; } -void FlattenConcat::terminate() {} +void FlattenConcat::terminate() noexcept {} -size_t FlattenConcat::getWorkspaceSize(int) const +size_t FlattenConcat::getWorkspaceSize(int) const noexcept { return 0; } -int FlattenConcat::enqueue(int batchSize, const void* const* inputs, void** outputs, void*, cudaStream_t stream) +int FlattenConcat::enqueue( + int batchSize, const void* const* inputs, void* const* outputs, void*, cudaStream_t stream) noexcept { - ASSERT(mConcatAxisID != 0); - // mCHW is the first input tensor - int numConcats = std::accumulate(mCHW.d, mCHW.d + mConcatAxisID - 1, 1, std::multiplies()); - - // Num concats will be proportional to number of samples in a batch - if (!mIgnoreBatch) + try { - numConcats *= batchSize; - } + ASSERT(mConcatAxisID != 0); + // mCHW is the first input tensor + int numConcats = std::accumulate(mCHW.d, mCHW.d + mConcatAxisID - 1, 1, std::multiplies()); - auto* output = static_cast(outputs[0]); - int offset = 0; - for (int i = 0; i < mNumInputs; ++i) - { - const auto* input = static_cast(inputs[i]); - for (int n = 0; n < numConcats; ++n) + // Num concats will be proportional to number of samples in a batch + if (!mIgnoreBatch) { - CUBLASASSERT(cublasScopy(mCublas, mInputConcatAxis[i], input + n * mInputConcatAxis[i], 1, - output + (n * mOutputConcatAxis + offset), 1)); + numConcats *= batchSize; } - offset += mInputConcatAxis[i]; + + auto* output = static_cast(outputs[0]); + int offset = 0; + for (int i = 0; i < mNumInputs; ++i) + { + const auto* input = static_cast(inputs[i]); + for (int n = 0; n < numConcats; ++n) + { + auto status = cublasScopy(mCublas, mInputConcatAxis[i], input + n * mInputConcatAxis[i], 1, + output + (n * mOutputConcatAxis + offset), 1); + + if (status != CUBLAS_STATUS_SUCCESS) + { + return STATUS_FAILURE; + } + } + offset += mInputConcatAxis[i]; + } + + return STATUS_SUCCESS; } - - return STATUS_SUCCESS; + catch (const std::exception& e) + { + caughtError(e); + } + return -1; } -size_t FlattenConcat::getSerializationSize() const +size_t FlattenConcat::getSerializationSize() const noexcept { - return sizeof(bool) + sizeof(int) * (3 + mNumInputs) + sizeof(nvinfer1::Dims) - + (sizeof(decltype(mCopySize)::value_type) * mNumInputs); + return sizeof(bool) + sizeof(int) * (3 + mNumInputs) + sizeof(nvinfer1::Dims) + (sizeof(decltype(mCopySize)::value_type) * mNumInputs); } -void FlattenConcat::serialize(void* buffer) const +void FlattenConcat::serialize(void* buffer) const noexcept { char* d = static_cast(buffer); const char* const a = d; @@ -186,39 +208,46 @@ void FlattenConcat::serialize(void* buffer) const // Attach the plugin object to an execution context and grant the plugin the access to some context resource. void FlattenConcat::attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) + cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept { mCublas = cublasContext; } // Detach the plugin object from its execution context. -void FlattenConcat::detachFromContext() {} +void FlattenConcat::detachFromContext() noexcept {} // Return true if output tensor is broadcast across a batch. -bool FlattenConcat::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const +bool FlattenConcat::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept { return false; } // Return true if plugin can use input that is broadcast across batch without replication. -bool FlattenConcat::canBroadcastInputAcrossBatch(int inputIndex) const +bool FlattenConcat::canBroadcastInputAcrossBatch(int inputIndex) const noexcept { return false; } // Set plugin namespace -void FlattenConcat::setPluginNamespace(const char* pluginNamespace) +void FlattenConcat::setPluginNamespace(const char* pluginNamespace) noexcept { - mPluginNamespace = pluginNamespace; + try + { + mPluginNamespace = pluginNamespace; + } + catch (const std::exception& e) + { + caughtError(e); + } } -const char* FlattenConcat::getPluginNamespace() const +const char* FlattenConcat::getPluginNamespace() const noexcept { return mPluginNamespace.c_str(); } // Return the DataType of the plugin output at the requested index -DataType FlattenConcat::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const +DataType FlattenConcat::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { ASSERT(index < 3); return DataType::kFLOAT; @@ -226,67 +255,82 @@ DataType FlattenConcat::getOutputDataType(int index, const nvinfer1::DataType* i void FlattenConcat::configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept { - ASSERT(nbOutputs == 1); - mCHW = inputDims[0]; - mNumInputs = nbInputs; - ASSERT(inputDims[0].nbDims == 3); - - mInputConcatAxis.resize(mNumInputs); - for (int i = 0; i < nbInputs; ++i) + try { - int flattenInput = 0; - ASSERT(inputDims[i].nbDims == 3); - if (mConcatAxisID != 1) + ASSERT(nbOutputs == 1); + mCHW = inputDims[0]; + mNumInputs = nbInputs; + ASSERT(inputDims[0].nbDims == 3); + + mInputConcatAxis.resize(mNumInputs); + for (int i = 0; i < nbInputs; ++i) { - ASSERT(inputDims[i].d[0] == inputDims[0].d[0]); + int flattenInput = 0; + ASSERT(inputDims[i].nbDims == 3); + if (mConcatAxisID != 1) + { + ASSERT(inputDims[i].d[0] == inputDims[0].d[0]); + } + if (mConcatAxisID != 2) + { + ASSERT(inputDims[i].d[1] == inputDims[0].d[1]); + } + if (mConcatAxisID != 3) + { + ASSERT(inputDims[i].d[2] == inputDims[0].d[2]); + } + flattenInput = inputDims[i].d[0] * inputDims[i].d[1] * inputDims[i].d[2]; + mInputConcatAxis[i] = flattenInput; + mOutputConcatAxis += mInputConcatAxis[i]; } - if (mConcatAxisID != 2) + + mCopySize.resize(mNumInputs); + for (int i = 0; i < nbInputs; ++i) { - ASSERT(inputDims[i].d[1] == inputDims[0].d[1]); + mCopySize[i] = inputDims[i].d[0] * inputDims[i].d[1] * inputDims[i].d[2] * sizeof(float); } - if (mConcatAxisID != 3) - { - ASSERT(inputDims[i].d[2] == inputDims[0].d[2]); - } - flattenInput = inputDims[i].d[0] * inputDims[i].d[1] * inputDims[i].d[2]; - mInputConcatAxis[i] = flattenInput; - mOutputConcatAxis += mInputConcatAxis[i]; } - - mCopySize.resize(mNumInputs); - for (int i = 0; i < nbInputs; ++i) + catch (const std::exception& e) { - mCopySize[i] = inputDims[i].d[0] * inputDims[i].d[1] * inputDims[i].d[2] * sizeof(float); + caughtError(e); } } -bool FlattenConcat::supportsFormat(DataType type, PluginFormat format) const +bool FlattenConcat::supportsFormat(DataType type, PluginFormat format) const noexcept { - return (type == DataType::kFLOAT && format == PluginFormat::kNCHW); + return (type == DataType::kFLOAT && format == PluginFormat::kLINEAR); } -const char* FlattenConcat::getPluginType() const +const char* FlattenConcat::getPluginType() const noexcept { return "FlattenConcat_TRT"; } -const char* FlattenConcat::getPluginVersion() const +const char* FlattenConcat::getPluginVersion() const noexcept { return "1"; } -void FlattenConcat::destroy() +void FlattenConcat::destroy() noexcept { delete this; } -IPluginV2Ext* FlattenConcat::clone() const +IPluginV2Ext* FlattenConcat::clone() const noexcept { - auto* plugin = new FlattenConcat( - mConcatAxisID, mIgnoreBatch, mNumInputs, mOutputConcatAxis, mInputConcatAxis.data(), mCopySize.data()); - plugin->setPluginNamespace(mPluginNamespace.c_str()); - return plugin; + try + { + auto* plugin = new FlattenConcat( + mConcatAxisID, mIgnoreBatch, mNumInputs, mOutputConcatAxis, mInputConcatAxis.data(), mCopySize.data()); + plugin->setPluginNamespace(mPluginNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } FlattenConcatPluginCreator::FlattenConcatPluginCreator() @@ -298,50 +342,66 @@ FlattenConcatPluginCreator::FlattenConcatPluginCreator() mFC.fields = mPluginAttributes.data(); } -const char* FlattenConcatPluginCreator::getPluginName() const +const char* FlattenConcatPluginCreator::getPluginName() const noexcept { return FLATTENCONCAT_PLUGIN_NAME; } -const char* FlattenConcatPluginCreator::getPluginVersion() const +const char* FlattenConcatPluginCreator::getPluginVersion() const noexcept { return FLATTENCONCAT_PLUGIN_VERSION; } -const PluginFieldCollection* FlattenConcatPluginCreator::getFieldNames() +const PluginFieldCollection* FlattenConcatPluginCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2Ext* FlattenConcatPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2Ext* FlattenConcatPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { - const PluginField* fields = fc->fields; - for (int i = 0; i < fc->nbFields; ++i) + try { - const char* attrName = fields[i].name; - if (!strcmp(attrName, "axis")) + const PluginField* fields = fc->fields; + for (int i = 0; i < fc->nbFields; ++i) { - ASSERT(fields[i].type == PluginFieldType::kINT32); - mConcatAxisID = *(static_cast(fields[i].data)); + const char* attrName = fields[i].name; + if (!strcmp(attrName, "axis")) + { + ASSERT(fields[i].type == PluginFieldType::kINT32); + mConcatAxisID = *(static_cast(fields[i].data)); + } + if (!strcmp(attrName, "ignoreBatch")) + { + ASSERT(fields[i].type == PluginFieldType::kINT32); + mIgnoreBatch = *(static_cast(fields[i].data)); + } } - if (!strcmp(attrName, "ignoreBatch")) - { - ASSERT(fields[i].type == PluginFieldType::kINT32); - mIgnoreBatch = *(static_cast(fields[i].data)); - } - } - auto* plugin = new FlattenConcat(mConcatAxisID, mIgnoreBatch); - plugin->setPluginNamespace(mNamespace.c_str()); - return plugin; + auto* plugin = new FlattenConcat(mConcatAxisID, mIgnoreBatch); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } IPluginV2Ext* FlattenConcatPluginCreator::deserializePlugin( - const char* name, const void* serialData, size_t serialLength) + const char* name, const void* serialData, size_t serialLength) noexcept { - // This object will be deleted when the network is destroyed, which will - // call Concat::destroy() - IPluginV2Ext* plugin = new FlattenConcat(serialData, serialLength); - plugin->setPluginNamespace(mNamespace.c_str()); - return plugin; + try + { + // This object will be deleted when the network is destroyed, which will + // call Concat::destroy() + IPluginV2Ext* plugin = new FlattenConcat(serialData, serialLength); + plugin->setPluginNamespace(mNamespace.c_str()); + return plugin; + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } diff --git a/plugin/flattenConcat/flattenConcat.h b/plugin/flattenConcat/flattenConcat.h index 6947bb89..85781ec1 100644 --- a/plugin/flattenConcat/flattenConcat.h +++ b/plugin/flattenConcat/flattenConcat.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_FLATTENCONCAT_PLUGIN_H #define TRT_FLATTENCONCAT_PLUGIN_H @@ -44,57 +43,57 @@ public: FlattenConcat() = delete; - int getNbOutputs() const override; + int getNbOutputs() const noexcept override; - Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) override; + Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept override; - int initialize() override; + int initialize() noexcept override; - void terminate() override; + void terminate() noexcept override; - size_t getWorkspaceSize(int) const override; + size_t getWorkspaceSize(int) const noexcept override; - int enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) override; + int enqueue(int batchSize, const void* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept override; - DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept override; - size_t getSerializationSize() const override; + size_t getSerializationSize() const noexcept override; - void serialize(void* buffer) const override; + void serialize(void* buffer) const noexcept override; - bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const override; + bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept override; - bool canBroadcastInputAcrossBatch(int inputIndex) const override; + bool canBroadcastInputAcrossBatch(int inputIndex) const noexcept override; void configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) override; + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept override; - bool supportsFormat(DataType type, PluginFormat format) const override; + bool supportsFormat(DataType type, PluginFormat format) const noexcept override; - void detachFromContext() override; + void detachFromContext() noexcept override; - const char* getPluginType() const override; + const char* getPluginType() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - void destroy() override; + void destroy() noexcept override; void attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) override; - IPluginV2Ext* clone() const override; + cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept override; + IPluginV2Ext* clone() const noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; private: - Weights copyToDevice(const void* hostData, size_t count); + Weights copyToDevice(const void* hostData, size_t count) noexcept; - void serializeFromDevice(char*& hostBuffer, Weights deviceWeights) const; + void serializeFromDevice(char*& hostBuffer, Weights deviceWeights) const noexcept; - Weights deserializeToDevice(const char*& hostBuffer, size_t count); + Weights deserializeToDevice(const char*& hostBuffer, size_t count) noexcept; std::vector mCopySize; std::vector mInputConcatAxis; @@ -112,15 +111,15 @@ public: ~FlattenConcatPluginCreator() override = default; - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const PluginFieldCollection* getFieldNames() override; + const PluginFieldCollection* getFieldNames() noexcept override; - IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) override; + IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; - IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; private: static PluginFieldCollection mFC; diff --git a/plugin/geluPlugin/geluKernel.cu b/plugin/geluPlugin/geluKernel.cu index 88c00a29..dd8a8532 100644 --- a/plugin/geluPlugin/geluKernel.cu +++ b/plugin/geluPlugin/geluKernel.cu @@ -101,14 +101,14 @@ __global__ void geluBiasKernel(const T a, const T b, const T c, T* output, const } } -void computeGeluBias( +int computeGeluBias( float* output, const float* input, const float* bias, const int ld, const int cols, cudaStream_t stream) { geluBiasKernel<<>>(A, B, C, output, input, bias, ld); - CHECK(cudaPeekAtLastError()); + return cudaPeekAtLastError(); } -void computeGeluBias( +int computeGeluBias( half* output, const half* input, const half* bias, const int ld, const int cols, cudaStream_t stream) { if (ld & 1) @@ -128,7 +128,7 @@ void computeGeluBias( geluBiasKernel<<>>(A2, B2, C2, output2, input2, bias2, ld2); } - CHECK(cudaPeekAtLastError()); + return cudaPeekAtLastError(); } } // namespace bert diff --git a/plugin/geluPlugin/geluPlugin.cpp b/plugin/geluPlugin/geluPlugin.cpp index 7a5a4299..7399d96e 100644 --- a/plugin/geluPlugin/geluPlugin.cpp +++ b/plugin/geluPlugin/geluPlugin.cpp @@ -33,8 +33,8 @@ namespace bert namespace { -static const char* GELU_PLUGIN_VERSION{"1"}; -static const char* GELU_PLUGIN_NAME{"CustomGeluPluginDynamic"}; +const char* GELU_PLUGIN_VERSION{"1"}; +const char* GELU_PLUGIN_NAME{"CustomGeluPluginDynamic"}; } // namespace // Static class fields initialization @@ -68,28 +68,28 @@ GeluPluginDynamic::GeluPluginDynamic(const std::string name, const void* data, s if (mHasBias) { - assert(mLd > 0); + ASSERT(mLd > 0); const char* d = static_cast(data); make_cuda_shared(mBiasDev, deserToDev(d, mLd * getElementSize(mType))); } } // IPluginV2DynamicExt Methods -nvinfer1::IPluginV2DynamicExt* GeluPluginDynamic::clone() const +nvinfer1::IPluginV2DynamicExt* GeluPluginDynamic::clone() const noexcept { gLogVerbose << "GeluPluginDynamic clone\n"; - auto plugin = new GeluPluginDynamic(*this); + auto* plugin = new GeluPluginDynamic(*this); plugin->setPluginNamespace(mNamespace.c_str()); return plugin; } nvinfer1::DimsExprs GeluPluginDynamic::getOutputDimensions( - int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) + int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) noexcept { return inputs[0]; } bool GeluPluginDynamic::supportsFormatCombination( - int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) + int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept { const PluginTensorDesc& input = inOut[0]; @@ -106,20 +106,20 @@ bool GeluPluginDynamic::supportsFormatCombination( } void GeluPluginDynamic::configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, - const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) + const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) noexcept { gLogVerbose << "GeluPluginDynamic configurePlugin\n"; assert(mType == in[0].desc.type); } size_t GeluPluginDynamic::getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int nbInputs, - const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const + const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const noexcept { return 0; } int GeluPluginDynamic::enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, const void* const* inputs, void* const* outputs, void* workspace, - cudaStream_t stream) + cudaStream_t stream) noexcept { const int inputVolume = volume(inputDesc[0].dims); @@ -136,7 +136,7 @@ int GeluPluginDynamic::enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const float* bias = static_cast(mBiasDev.get()); const int cols = inputVolume / mLd; const int rows = mLd; - computeGeluBias(output, input, bias, rows, cols, stream); + status = computeGeluBias(output, input, bias, rows, cols, stream); } else { @@ -154,7 +154,7 @@ int GeluPluginDynamic::enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const half* bias = static_cast(mBiasDev.get()); const int cols = inputVolume / mLd; const int rows = mLd; - computeGeluBias(output, input, bias, rows, cols, stream); + status = computeGeluBias(output, input, bias, rows, cols, stream); } else { @@ -163,7 +163,7 @@ int GeluPluginDynamic::enqueue(const nvinfer1::PluginTensorDesc* inputDesc, } else { - assert(false); + return STATUS_FAILURE; } return status; @@ -171,7 +171,7 @@ int GeluPluginDynamic::enqueue(const nvinfer1::PluginTensorDesc* inputDesc, // IPluginV2Ext Methods nvinfer1::DataType GeluPluginDynamic::getOutputDataType( - int index, const nvinfer1::DataType* inputTypes, int nbInputs) const + int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { assert(index == 0); assert(inputTypes[0] == DataType::kFLOAT || inputTypes[0] == DataType::kHALF); @@ -180,40 +180,40 @@ nvinfer1::DataType GeluPluginDynamic::getOutputDataType( // IPluginV2 Methods -const char* GeluPluginDynamic::getPluginType() const +const char* GeluPluginDynamic::getPluginType() const noexcept { return GELU_PLUGIN_NAME; } -const char* GeluPluginDynamic::getPluginVersion() const +const char* GeluPluginDynamic::getPluginVersion() const noexcept { return GELU_PLUGIN_VERSION; } -int GeluPluginDynamic::getNbOutputs() const +int GeluPluginDynamic::getNbOutputs() const noexcept { return 1; } -int GeluPluginDynamic::initialize() +int GeluPluginDynamic::initialize() noexcept { gLogVerbose << "GeluPluginDynamic initalize\n"; return 0; } -void GeluPluginDynamic::terminate() +void GeluPluginDynamic::terminate() noexcept { gLogVerbose << "GeluPluginDynamic terminate\n"; } -size_t GeluPluginDynamic::getSerializationSize() const +size_t GeluPluginDynamic::getSerializationSize() const noexcept { const size_t wordSize = getElementSize(mType); const size_t biasSize = mHasBias ? mLd * wordSize : 0; return sizeof(mType) + sizeof(mHasBias) + sizeof(mLd) + biasSize; } -void GeluPluginDynamic::serialize(void* buffer) const +void GeluPluginDynamic::serialize(void* buffer) const noexcept { serialize_value(&buffer, mType); serialize_value(&buffer, mLd); @@ -226,7 +226,7 @@ void GeluPluginDynamic::serialize(void* buffer) const } } -void GeluPluginDynamic::destroy() +void GeluPluginDynamic::destroy() noexcept { gLogVerbose << "GeluPluginDynamic destroy\n"; // This gets called when the network containing plugin is destroyed @@ -234,12 +234,12 @@ void GeluPluginDynamic::destroy() delete this; } -void GeluPluginDynamic::setPluginNamespace(const char* libNamespace) +void GeluPluginDynamic::setPluginNamespace(const char* libNamespace) noexcept { mNamespace = libNamespace; } -const char* GeluPluginDynamic::getPluginNamespace() const +const char* GeluPluginDynamic::getPluginNamespace() const noexcept { return mNamespace.c_str(); } @@ -253,65 +253,82 @@ GeluPluginDynamicCreator::GeluPluginDynamicCreator() mFC.fields = mPluginAttributes.data(); } -const char* GeluPluginDynamicCreator::getPluginName() const +const char* GeluPluginDynamicCreator::getPluginName() const noexcept { return GELU_PLUGIN_NAME; } -const char* GeluPluginDynamicCreator::getPluginVersion() const +const char* GeluPluginDynamicCreator::getPluginVersion() const noexcept { return GELU_PLUGIN_VERSION; } -const PluginFieldCollection* GeluPluginDynamicCreator::getFieldNames() +const PluginFieldCollection* GeluPluginDynamicCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2* GeluPluginDynamicCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2* GeluPluginDynamicCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { - gLogVerbose << "GeluPluginDynamicCreator createPlugin\n"; - - Weights bias{DataType::kFLOAT, nullptr, 0}; - int typeId = -1; - for (int i = 0; i < fc->nbFields; i++) + try { - std::string field_name(fc->fields[i].name); + gLogVerbose << "GeluPluginDynamicCreator createPlugin\n"; - if (field_name.compare("type_id") == 0) + Weights bias{DataType::kFLOAT, nullptr, 0}; + int typeId = -1; + for (int i = 0; i < fc->nbFields; i++) { - typeId = *static_cast(fc->fields[i].data); + std::string field_name(fc->fields[i].name); + + if (field_name.compare("type_id") == 0) + { + typeId = *static_cast(fc->fields[i].data); + } + if (field_name.compare("bias") == 0) + { + bias.values = fc->fields[i].data; + bias.count = fc->fields[i].length; + bias.type = fieldTypeToDataType(fc->fields[i].type); + } } - if (field_name.compare("bias") == 0) + + if (typeId < 0 || typeId > 3) { - bias.values = fc->fields[i].data; - bias.count = fc->fields[i].length; - bias.type = fieldTypeToDataType(fc->fields[i].type); + gLogError << "GeluPluginDynamicCreator: invalid typeId " << typeId << std::endl; + return nullptr; } + + return new GeluPluginDynamic(name, static_cast(typeId), bias); } - - if (typeId < 0 || typeId > 3) + catch (const std::exception& e) { - gLogError << "GeluPluginDynamicCreator: invalid typeId " << typeId << std::endl; - return nullptr; + caughtError(e); } - - return new GeluPluginDynamic(name, static_cast(typeId), bias); + return nullptr; } -IPluginV2* GeluPluginDynamicCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) +IPluginV2* GeluPluginDynamicCreator::deserializePlugin( + const char* name, const void* serialData, size_t serialLength) noexcept { // This object will be deleted when the network is destroyed, which will // call GeluPluginDynamic::destroy() - return new GeluPluginDynamic(name, serialData, serialLength); + try + { + return new GeluPluginDynamic(name, serialData, serialLength); + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } -void GeluPluginDynamicCreator::setPluginNamespace(const char* libNamespace) +void GeluPluginDynamicCreator::setPluginNamespace(const char* libNamespace) noexcept { mNamespace = libNamespace; } -const char* GeluPluginDynamicCreator::getPluginNamespace() const +const char* GeluPluginDynamicCreator::getPluginNamespace() const noexcept { return mNamespace.c_str(); } diff --git a/plugin/geluPlugin/geluPlugin.h b/plugin/geluPlugin/geluPlugin.h index 50ef5820..bf6d0ba9 100644 --- a/plugin/geluPlugin/geluPlugin.h +++ b/plugin/geluPlugin/geluPlugin.h @@ -20,8 +20,8 @@ #ifndef TRT_GELU_PLUGIN_H #define TRT_GELU_PLUGIN_H -#include "NvInferPlugin.h" #include "bertCommon.h" +#include "NvInferPlugin.h" #include #include @@ -32,10 +32,10 @@ int computeGelu(cudaStream_t stream, int n, const float* input, float* output); int computeGelu(cudaStream_t stream, int n, const half* input, half* output); -void computeGeluBias( +int computeGeluBias( float* output, const float* input, const float* bias, const int ld, const int cols, cudaStream_t stream); -void computeGeluBias( +int computeGeluBias( half* output, const half* input, const half* bias, const int ld, const int cols, cudaStream_t stream); class GeluPluginDynamic : public nvinfer1::IPluginV2DynamicExt @@ -50,32 +50,33 @@ public: GeluPluginDynamic() = delete; // IPluginV2DynamicExt Methods - nvinfer1::IPluginV2DynamicExt* clone() const override; - nvinfer1::DimsExprs getOutputDimensions( - int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) override; + nvinfer1::IPluginV2DynamicExt* clone() const noexcept override; + nvinfer1::DimsExprs getOutputDimensions(int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, + nvinfer1::IExprBuilder& exprBuilder) noexcept override; bool supportsFormatCombination( - int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) override; + int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept override; void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, - const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) override; + const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) noexcept override; size_t getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int nbInputs, - const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const override; + const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const noexcept override; int enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) override; + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; // IPluginV2Ext Methods - nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const + noexcept override; // IPluginV2 Methods - const char* getPluginType() const override; - const char* getPluginVersion() const override; - int getNbOutputs() const override; - int initialize() override; - void terminate() override; - size_t getSerializationSize() const override; - void serialize(void* buffer) const override; - void destroy() override; - void setPluginNamespace(const char* pluginNamespace) override; - const char* getPluginNamespace() const override; + const char* getPluginType() const noexcept override; + const char* getPluginVersion() const noexcept override; + int getNbOutputs() const noexcept override; + int initialize() noexcept override; + void terminate() noexcept override; + size_t getSerializationSize() const noexcept override; + void serialize(void* buffer) const noexcept override; + void destroy() noexcept override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; + const char* getPluginNamespace() const noexcept override; private: const std::string mLayerName; @@ -85,16 +86,6 @@ private: bool mHasBias; bert::cuda_shared_ptr mBiasDev; size_t mLd; - -protected: - // To prevent compiler warnings. - using nvinfer1::IPluginV2DynamicExt::canBroadcastInputAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::configurePlugin; - using nvinfer1::IPluginV2DynamicExt::enqueue; - using nvinfer1::IPluginV2DynamicExt::getOutputDimensions; - using nvinfer1::IPluginV2DynamicExt::getWorkspaceSize; - using nvinfer1::IPluginV2DynamicExt::isOutputBroadcastAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::supportsFormat; }; class GeluPluginDynamicCreator : public nvinfer1::IPluginCreator @@ -102,19 +93,19 @@ class GeluPluginDynamicCreator : public nvinfer1::IPluginCreator public: GeluPluginDynamicCreator(); - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const nvinfer1::PluginFieldCollection* getFieldNames() override; + const nvinfer1::PluginFieldCollection* getFieldNames() noexcept override; - nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) override; + nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) noexcept override; - nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; private: static nvinfer1::PluginFieldCollection mFC; diff --git a/plugin/generateDetectionPlugin/generateDetectionPlugin.cpp b/plugin/generateDetectionPlugin/generateDetectionPlugin.cpp index 521f9c5a..d2b1816f 100644 --- a/plugin/generateDetectionPlugin/generateDetectionPlugin.cpp +++ b/plugin/generateDetectionPlugin/generateDetectionPlugin.cpp @@ -160,7 +160,7 @@ void GenerateDetection::destroy() noexcept bool GenerateDetection::supportsFormat(DataType type, PluginFormat format) const noexcept { - return (type == DataType::kFLOAT && format == PluginFormat::kNCHW); + return (type == DataType::kFLOAT && format == PluginFormat::kLINEAR); }; const char* GenerateDetection::getPluginType() const noexcept @@ -270,8 +270,8 @@ Dims GenerateDetection::getOutputDimensions(int index, const Dims* inputs, int n return detections; } -int GenerateDetection::enqueue( - int batch_size, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) noexcept +int32_t GenerateDetection::enqueue( + int32_t batch_size, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { void* detections = outputs[0]; diff --git a/plugin/generateDetectionPlugin/generateDetectionPlugin.h b/plugin/generateDetectionPlugin/generateDetectionPlugin.h index 2a8aba3c..667e25b0 100644 --- a/plugin/generateDetectionPlugin/generateDetectionPlugin.h +++ b/plugin/generateDetectionPlugin/generateDetectionPlugin.h @@ -55,8 +55,8 @@ public: size_t getWorkspaceSize(int maxBatchSize) const noexcept override; - int enqueue( - int batch_size, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) noexcept override; + int32_t enqueue( + int32_t batch_size, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; size_t getSerializationSize() const noexcept override; diff --git a/plugin/gridAnchorPlugin/gridAnchorPlugin.cpp b/plugin/gridAnchorPlugin/gridAnchorPlugin.cpp index cb52a4e1..fad1c5cc 100644 --- a/plugin/gridAnchorPlugin/gridAnchorPlugin.cpp +++ b/plugin/gridAnchorPlugin/gridAnchorPlugin.cpp @@ -34,8 +34,8 @@ PluginFieldCollection GridAnchorBasePluginCreator::mFC{}; std::vector GridAnchorBasePluginCreator::mPluginAttributes; GridAnchorGenerator::GridAnchorGenerator(const GridAnchorParameters* paramIn, int numLayers, const char* name) - : mNumLayers(numLayers) - , mPluginName(name) + : mPluginName(name) + , mNumLayers(numLayers) { CUASSERT(cudaMallocHost((void**) &mNumPriors, mNumLayers * sizeof(int))); CUASSERT(cudaMallocHost((void**) &mDeviceWidths, mNumLayers * sizeof(Weights))); @@ -170,33 +170,33 @@ GridAnchorGenerator::~GridAnchorGenerator() CUERRORMSG(cudaFreeHost(mDeviceHeights)); } -int GridAnchorGenerator::getNbOutputs() const +int GridAnchorGenerator::getNbOutputs() const noexcept { return mNumLayers; } -Dims GridAnchorGenerator::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) +Dims GridAnchorGenerator::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept { // Particularity of the PriorBox layer: no batchSize dimension needed // 2 channels. First channel stores the mean of each prior coordinate. // Second channel stores the variance of each prior coordinate. - return DimsCHW(2, mParam[index].H * mParam[index].W * mNumPriors[index] * 4, 1); + return Dims3(2, mParam[index].H * mParam[index].W * mNumPriors[index] * 4, 1); } -int GridAnchorGenerator::initialize() +int GridAnchorGenerator::initialize() noexcept { return STATUS_SUCCESS; } -void GridAnchorGenerator::terminate() {} +void GridAnchorGenerator::terminate() noexcept {} -size_t GridAnchorGenerator::getWorkspaceSize(int maxBatchSize) const +size_t GridAnchorGenerator::getWorkspaceSize(int maxBatchSize) const noexcept { return 0; } int GridAnchorGenerator::enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) + int batchSize, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { // Generate prior boxes for each layer for (int id = 0; id < mNumLayers; id++) @@ -204,12 +204,15 @@ int GridAnchorGenerator::enqueue( void* outputData = outputs[id]; pluginStatus_t status = anchorGridInference( stream, mParam[id], mNumPriors[id], mDeviceWidths[id].values, mDeviceHeights[id].values, outputData); - ASSERT(status == STATUS_SUCCESS); + if (status != STATUS_SUCCESS) + { + return status; + } } return STATUS_SUCCESS; } -size_t GridAnchorGenerator::getSerializationSize() const +size_t GridAnchorGenerator::getSerializationSize() const noexcept { size_t sum = sizeof(int); // mNumLayers for (int i = 0; i < mNumLayers; i++) @@ -223,7 +226,7 @@ size_t GridAnchorGenerator::getSerializationSize() const return sum; } -void GridAnchorGenerator::serialize(void* buffer) const +void GridAnchorGenerator::serialize(void* buffer) const noexcept { char *d = reinterpret_cast(buffer), *a = d; write(d, mNumLayers); @@ -251,7 +254,7 @@ void GridAnchorGenerator::serialize(void* buffer) const ASSERT(d == a + getSerializationSize()); } -Weights GridAnchorGenerator::copyToDevice(const void* hostData, size_t count) +Weights GridAnchorGenerator::copyToDevice(const void* hostData, size_t count) noexcept { void* deviceData; CUASSERT(cudaMalloc(&deviceData, count * sizeof(float))); @@ -259,47 +262,47 @@ Weights GridAnchorGenerator::copyToDevice(const void* hostData, size_t count) return Weights{DataType::kFLOAT, deviceData, int64_t(count)}; } -void GridAnchorGenerator::serializeFromDevice(char*& hostBuffer, Weights deviceWeights) const +void GridAnchorGenerator::serializeFromDevice(char*& hostBuffer, Weights deviceWeights) const noexcept { cudaMemcpy(hostBuffer, deviceWeights.values, deviceWeights.count * sizeof(float), cudaMemcpyDeviceToHost); hostBuffer += deviceWeights.count * sizeof(float); } -Weights GridAnchorGenerator::deserializeToDevice(const char*& hostBuffer, size_t count) +Weights GridAnchorGenerator::deserializeToDevice(const char*& hostBuffer, size_t count) noexcept { Weights w = copyToDevice(hostBuffer, count); hostBuffer += count * sizeof(float); return w; } -bool GridAnchorGenerator::supportsFormat(DataType type, PluginFormat format) const +bool GridAnchorGenerator::supportsFormat(DataType type, PluginFormat format) const noexcept { - return (type == DataType::kFLOAT && format == PluginFormat::kNCHW); + return (type == DataType::kFLOAT && format == PluginFormat::kLINEAR); } -const char* GridAnchorGenerator::getPluginType() const +const char* GridAnchorGenerator::getPluginType() const noexcept { return mPluginName.c_str(); } -const char* GridAnchorGenerator::getPluginVersion() const +const char* GridAnchorGenerator::getPluginVersion() const noexcept { return GRID_ANCHOR_PLUGIN_VERSION; } // Set plugin namespace -void GridAnchorGenerator::setPluginNamespace(const char* pluginNamespace) +void GridAnchorGenerator::setPluginNamespace(const char* pluginNamespace) noexcept { mPluginNamespace = pluginNamespace; } -const char* GridAnchorGenerator::getPluginNamespace() const +const char* GridAnchorGenerator::getPluginNamespace() const noexcept { return mPluginNamespace.c_str(); } #include // Return the DataType of the plugin output at the requested index -DataType GridAnchorGenerator::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const +DataType GridAnchorGenerator::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { ASSERT(index < mNumLayers); return DataType::kFLOAT; @@ -307,13 +310,13 @@ DataType GridAnchorGenerator::getOutputDataType(int index, const nvinfer1::DataT // Return true if output tensor is broadcast across a batch. bool GridAnchorGenerator::isOutputBroadcastAcrossBatch( - int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const + int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept { return false; } // Return true if plugin can use input that is broadcast across batch without replication. -bool GridAnchorGenerator::canBroadcastInputAcrossBatch(int inputIndex) const +bool GridAnchorGenerator::canBroadcastInputAcrossBatch(int inputIndex) const noexcept { return false; } @@ -321,7 +324,7 @@ bool GridAnchorGenerator::canBroadcastInputAcrossBatch(int inputIndex) const // Configure the layer with input and output data types. void GridAnchorGenerator::configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept { ASSERT(nbOutputs == mNumLayers); ASSERT(outputDims[0].nbDims == 3); @@ -329,19 +332,19 @@ void GridAnchorGenerator::configurePlugin(const Dims* inputDims, int nbInputs, c // Attach the plugin object to an execution context and grant the plugin the access to some context resource. void GridAnchorGenerator::attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) + cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept { } // Detach the plugin object from its execution context. -void GridAnchorGenerator::detachFromContext() {} +void GridAnchorGenerator::detachFromContext() noexcept {} -void GridAnchorGenerator::destroy() +void GridAnchorGenerator::destroy() noexcept { delete this; } -IPluginV2Ext* GridAnchorGenerator::clone() const +IPluginV2Ext* GridAnchorGenerator::clone() const noexcept { IPluginV2Ext* plugin = new GridAnchorGenerator(mParam.data(), mNumLayers, mPluginName.c_str()); plugin->setPluginNamespace(mPluginNamespace.c_str()); @@ -361,22 +364,22 @@ GridAnchorBasePluginCreator::GridAnchorBasePluginCreator() mFC.fields = mPluginAttributes.data(); } -const char* GridAnchorBasePluginCreator::getPluginName() const +const char* GridAnchorBasePluginCreator::getPluginName() const noexcept { return mPluginName.c_str(); } -const char* GridAnchorBasePluginCreator::getPluginVersion() const +const char* GridAnchorBasePluginCreator::getPluginVersion() const noexcept { return GRID_ANCHOR_PLUGIN_VERSION; } -const PluginFieldCollection* GridAnchorBasePluginCreator::getFieldNames() +const PluginFieldCollection* GridAnchorBasePluginCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2Ext* GridAnchorBasePluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2Ext* GridAnchorBasePluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { float minScale = 0.2F, maxScale = 0.95F; int numLayers = 6; @@ -484,8 +487,7 @@ IPluginV2Ext* GridAnchorBasePluginCreator::createPlugin(const char* name, const return obj; } -IPluginV2Ext* GridAnchorBasePluginCreator::deserializePlugin( - const char* name, const void* serialData, size_t serialLength) +IPluginV2Ext* GridAnchorBasePluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept { // This object will be deleted when the network is destroyed, which will // call GridAnchor::destroy() diff --git a/plugin/gridAnchorPlugin/gridAnchorPlugin.h b/plugin/gridAnchorPlugin/gridAnchorPlugin.h index 12e9a834..15c1a283 100644 --- a/plugin/gridAnchorPlugin/gridAnchorPlugin.h +++ b/plugin/gridAnchorPlugin/gridAnchorPlugin.h @@ -36,61 +36,61 @@ public: ~GridAnchorGenerator() override; - int getNbOutputs() const override; + int getNbOutputs() const noexcept override; - Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) override; + Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept override; - int initialize() override; + int initialize() noexcept override; - void terminate() override; + void terminate() noexcept override; - size_t getWorkspaceSize(int maxBatchSize) const override; + size_t getWorkspaceSize(int maxBatchSize) const noexcept override; - int enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) override; + int enqueue(int batchSize, const void* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept override; - size_t getSerializationSize() const override; + size_t getSerializationSize() const noexcept override; - void serialize(void* buffer) const override; + void serialize(void* buffer) const noexcept override; - bool supportsFormat(DataType type, PluginFormat format) const override; + bool supportsFormat(DataType type, PluginFormat format) const noexcept override; - const char* getPluginType() const override; + const char* getPluginType() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - void destroy() override; + void destroy() noexcept override; - IPluginV2Ext* clone() const override; + IPluginV2Ext* clone() const noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; - DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept override; - bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const override; + bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept override; - bool canBroadcastInputAcrossBatch(int inputIndex) const override; + bool canBroadcastInputAcrossBatch(int inputIndex) const noexcept override; void attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) override; + cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept override; void configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) override; + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept override; - void detachFromContext() override; + void detachFromContext() noexcept override; protected: std::string mPluginName; private: - Weights copyToDevice(const void* hostData, size_t count); + Weights copyToDevice(const void* hostData, size_t count) noexcept; - void serializeFromDevice(char*& hostBuffer, Weights deviceWeights) const; + void serializeFromDevice(char*& hostBuffer, Weights deviceWeights) const noexcept; - Weights deserializeToDevice(const char*& hostBuffer, size_t count); + Weights deserializeToDevice(const char*& hostBuffer, size_t count) noexcept; int mNumLayers; std::vector mParam; @@ -106,15 +106,15 @@ public: ~GridAnchorBasePluginCreator() override = default; - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const PluginFieldCollection* getFieldNames() override; + const PluginFieldCollection* getFieldNames() noexcept override; - IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) override; + IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; - IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; protected: std::string mPluginName; diff --git a/plugin/groupNormalizationPlugin/groupNormalizationKernel.cu b/plugin/groupNormalizationPlugin/groupNormalizationKernel.cu index b3274830..6197257c 100644 --- a/plugin/groupNormalizationPlugin/groupNormalizationKernel.cu +++ b/plugin/groupNormalizationPlugin/groupNormalizationKernel.cu @@ -43,7 +43,7 @@ __global__ void scaleShiftChannelsInplaceKernel(T* inOut, const int ld, const fl } template -void scaleShiftChannelsInplace(T* inOut, const int B, const int C, const int channelVolume, const float* beta, +cudaError_t scaleShiftChannelsInplace(T* inOut, const int B, const int C, const int channelVolume, const float* beta, const float* gamma, cudaStream_t stream) { @@ -53,10 +53,10 @@ void scaleShiftChannelsInplace(T* inOut, const int B, const int C, const int cha scaleShiftChannelsInplaceKernel<<>>(inOut, channelVolume, beta, gamma); - CUASSERT(cudaPeekAtLastError()); + return cudaPeekAtLastError(); } -template void scaleShiftChannelsInplace(float* inOut, const int B, const int C, const int channelVolume, const float* beta, +template cudaError_t scaleShiftChannelsInplace(float* inOut, const int B, const int C, const int channelVolume, const float* beta, const float* gamma, cudaStream_t stream); } /* plugin */ } /* nvinfer1 */ diff --git a/plugin/groupNormalizationPlugin/groupNormalizationPlugin.cpp b/plugin/groupNormalizationPlugin/groupNormalizationPlugin.cpp index 06bf6e80..062e47b1 100644 --- a/plugin/groupNormalizationPlugin/groupNormalizationPlugin.cpp +++ b/plugin/groupNormalizationPlugin/groupNormalizationPlugin.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ -#include "groupNormalizationPlugin.h" #include #include +#include "groupNormalizationPlugin.h" using namespace nvinfer1; using nvinfer1::plugin::GroupNormalizationPlugin; @@ -45,14 +45,14 @@ std::vector GroupNormalizationPluginCreator::mPluginAttri REGISTER_TENSORRT_PLUGIN(GroupNormalizationPluginCreator); GroupNormalizationPlugin::GroupNormalizationPlugin(float epsilon, int nbGroups) - : mEpsilon(epsilon) - , mNbGroups(nbGroups) + : mEpsilon(epsilon), + mNbGroups(nbGroups) { // Number of groups should be positive assert(nbGroups > 0); } -int GroupNormalizationPlugin::initialize() +int GroupNormalizationPlugin::initialize() noexcept { return 0; } @@ -64,23 +64,23 @@ GroupNormalizationPlugin::GroupNormalizationPlugin(const void* data, size_t leng deserialize_value(&data, &length, &mNbGroups); } -const char* GroupNormalizationPlugin::getPluginType() const +const char* GroupNormalizationPlugin::getPluginType() const noexcept { return GROUP_NORM_NAME; } -const char* GroupNormalizationPlugin::getPluginVersion() const +const char* GroupNormalizationPlugin::getPluginVersion() const noexcept { return GROUP_NORM_VERSION; } -int GroupNormalizationPlugin::getNbOutputs() const +int GroupNormalizationPlugin::getNbOutputs() const noexcept { return 1; } nvinfer1::DimsExprs GroupNormalizationPlugin::getOutputDimensions( - int index, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) + int index, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) noexcept { // Input (from previous layer), scale and bias are the three inputs to the plugin. assert(nbInputs == 3); @@ -89,8 +89,7 @@ nvinfer1::DimsExprs GroupNormalizationPlugin::getOutputDimensions( return output; } -void GroupNormalizationPlugin::attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) +void GroupNormalizationPlugin::attachToContext(cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept { _cudnn_handle = cudnnContext; cudnnCreateTensorDescriptor(&desc); @@ -98,7 +97,7 @@ void GroupNormalizationPlugin::attachToContext( } // Detach the plugin object from its execution context. -void GroupNormalizationPlugin::detachFromContext() +void GroupNormalizationPlugin::detachFromContext() noexcept { cudnnDestroyTensorDescriptor(desc); cudnnDestroyTensorDescriptor(bnDesc); @@ -106,7 +105,7 @@ void GroupNormalizationPlugin::detachFromContext() int GroupNormalizationPlugin::enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, const void* const* inputs, void* const* outputs, void* workspace, - cudaStream_t stream) + cudaStream_t stream) noexcept { // Get the input dimensions nvinfer1::Dims input_dims = inputDesc[0].dims; @@ -116,24 +115,23 @@ int GroupNormalizationPlugin::enqueue(const nvinfer1::PluginTensorDesc* inputDes // Calculate size of each group int groupSize = nbChannels / mNbGroups; - mChannelVolume - = std::accumulate(input_dims.d + 2, input_dims.d + inputDesc[0].dims.nbDims, 1, std::multiplies()); + mChannelVolume = std::accumulate(input_dims.d + 2, input_dims.d + inputDesc[0].dims.nbDims, 1, std::multiplies()); CHECK_CUDNN(cudnnSetTensor4dDescriptor(desc, // descriptor - CUDNN_TENSOR_NCHW, // tensor format - CUDNN_DATA_FLOAT, // type - 1, // Batchsize - batchSize * mNbGroups, // Channels - groupSize, // Height - mChannelVolume // Width + CUDNN_TENSOR_NCHW, // tensor format + CUDNN_DATA_FLOAT, // type + 1, // Batchsize + batchSize * mNbGroups, // Channels + groupSize, // Height + mChannelVolume // Width )); - cudnnDeriveBNTensorDescriptor(bnDesc, desc, CUDNN_BATCHNORM_SPATIAL); + CHECK_CUDNN(cudnnDeriveBNTensorDescriptor(bnDesc, desc, CUDNN_BATCHNORM_SPATIAL)); CHECK_CUDNN(cudnnSetStream(_cudnn_handle, stream)); // Reshape the data according in the cudnnSetTensor4dDescriptor. - float a = 1.f; - float b = 0.f; + float a = 1.F; + float b = 0.F; CHECK_CUDNN(cudnnBatchNormalizationForwardTraining(_cudnn_handle, // handle CUDNN_BATCHNORM_SPATIAL, // BatchNormMode_t, try also non persistent &a, // @@ -154,43 +152,41 @@ int GroupNormalizationPlugin::enqueue(const nvinfer1::PluginTensorDesc* inputDes )); float* output = static_cast(outputs[0]); - scaleShiftChannelsInplace(output, batchSize, nbChannels, mChannelVolume, static_cast(inputs[2]), - static_cast(inputs[1]), stream); // mBetaDev, mGammaDev, - return 0; + return scaleShiftChannelsInplace(output, batchSize, nbChannels, mChannelVolume, static_cast(inputs[2]), static_cast(inputs[1]), stream); //mBetaDev, mGammaDev, } -size_t GroupNormalizationPlugin::getSerializationSize() const +size_t GroupNormalizationPlugin::getSerializationSize() const noexcept { return sizeof(mNbGroups) + sizeof(mEpsilon); } -void GroupNormalizationPlugin::serialize(void* buffer) const +void GroupNormalizationPlugin::serialize(void* buffer) const noexcept { serialize_value(&buffer, mEpsilon); serialize_value(&buffer, mNbGroups); } bool GroupNormalizationPlugin::supportsFormatCombination( - int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) + int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept { assert(inOut && pos < (nbInputs + nbOutputs)); - return ((inOut[pos].type == nvinfer1::DataType::kFLOAT) && inOut[pos].format == nvinfer1::PluginFormat::kNCHW + return ((inOut[pos].type == nvinfer1::DataType::kFLOAT) && inOut[pos].format == nvinfer1::PluginFormat::kLINEAR && inOut[pos].type == inOut[0].type); } -void GroupNormalizationPlugin::terminate() +void GroupNormalizationPlugin::terminate() noexcept { cudaFree(bnScale); cudaFree(bnBias); } -void GroupNormalizationPlugin::destroy() +void GroupNormalizationPlugin::destroy() noexcept { // This gets called when the network containing plugin is destroyed delete this; } -IPluginV2DynamicExt* GroupNormalizationPlugin::clone() const +IPluginV2DynamicExt* GroupNormalizationPlugin::clone() const noexcept { auto* plugin = new GroupNormalizationPlugin(mEpsilon, mNbGroups); plugin->setPluginNamespace(mPluginNamespace); @@ -198,16 +194,16 @@ IPluginV2DynamicExt* GroupNormalizationPlugin::clone() const } void GroupNormalizationPlugin::configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, - const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) + const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) noexcept { for (int i = 0; i < nbInputs; i++) { - for (int j = 0; j < in[0].desc.dims.nbDims; j++) - { - // Do not support dynamic dimensions - assert(in[0].desc.dims.d[j] != -1); - } + for (int j = 0; j < in[0].desc.dims.nbDims; j++) + { + // Do not support dynamic dimensions + assert(in[0].desc.dims.d[j] != -1); + } } int batchSize = in[0].desc.dims.d[0]; @@ -218,32 +214,32 @@ void GroupNormalizationPlugin::configurePlugin(const nvinfer1::DynamicPluginTens cudaMalloc(&bnBias, batchSize * nbChannels * sizeof(float)); // allot ones and zeros to bn parameters - std::vector ones(nbChannels, 1.f); + std::vector ones(nbChannels, 1.F); cudaMemcpy(bnScale, ones.data(), nbChannels * sizeof(float), cudaMemcpyHostToDevice); - std::vector zeroes(nbChannels, 0.f); + std::vector zeroes(nbChannels, 0.F); cudaMemcpy(bnBias, zeroes.data(), nbChannels * sizeof(float), cudaMemcpyHostToDevice); } nvinfer1::DataType GroupNormalizationPlugin::getOutputDataType( - int index, const nvinfer1::DataType* inputTypes, int nbInputs) const + int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { assert(inputTypes && nbInputs > 0 && index == 0); return inputTypes[0]; } size_t GroupNormalizationPlugin::getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int nbInputs, - const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const + const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const noexcept { return 0; } -void GroupNormalizationPlugin::setPluginNamespace(const char* libNamespace) +void GroupNormalizationPlugin::setPluginNamespace(const char* libNamespace) noexcept { mPluginNamespace = libNamespace; } -const char* GroupNormalizationPlugin::getPluginNamespace() const +const char* GroupNormalizationPlugin::getPluginNamespace() const noexcept { return mPluginNamespace; } @@ -257,36 +253,36 @@ GroupNormalizationPluginCreator::GroupNormalizationPluginCreator() mFC.fields = mPluginAttributes.data(); } -const char* GroupNormalizationPluginCreator::getPluginName() const +const char* GroupNormalizationPluginCreator::getPluginName() const noexcept { return GROUP_NORM_NAME; } -const char* GroupNormalizationPluginCreator::getPluginVersion() const +const char* GroupNormalizationPluginCreator::getPluginVersion() const noexcept { return GROUP_NORM_VERSION; } -const PluginFieldCollection* GroupNormalizationPluginCreator::getFieldNames() +const PluginFieldCollection* GroupNormalizationPluginCreator::getFieldNames() noexcept { return &mFC; } -const char* GroupNormalizationPluginCreator::getPluginNamespace() const +const char* GroupNormalizationPluginCreator::getPluginNamespace() const noexcept { return mNamespace.c_str(); } -void GroupNormalizationPluginCreator::setPluginNamespace(const char* libNamespace) +void GroupNormalizationPluginCreator::setPluginNamespace(const char* libNamespace) noexcept { mNamespace = libNamespace; } -IPluginV2DynamicExt* GroupNormalizationPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2DynamicExt* GroupNormalizationPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { // Set default values int nbGroups{1}; - float epsilon{0.00001f}; + float epsilon{0.00001F}; for (int i = 0; i < fc->nbFields; i++) { std::string field_name(fc->fields[i].name); @@ -306,8 +302,7 @@ IPluginV2DynamicExt* GroupNormalizationPluginCreator::createPlugin(const char* n return plugin; } -IPluginV2DynamicExt* GroupNormalizationPluginCreator::deserializePlugin( - const char* name, const void* serialData, size_t serialLength) +IPluginV2DynamicExt* GroupNormalizationPluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept { GroupNormalizationPlugin* plugin = new GroupNormalizationPlugin(serialData, serialLength); plugin->setPluginNamespace(mNamespace.c_str()); diff --git a/plugin/groupNormalizationPlugin/groupNormalizationPlugin.h b/plugin/groupNormalizationPlugin/groupNormalizationPlugin.h index 6402752d..c1d00771 100644 --- a/plugin/groupNormalizationPlugin/groupNormalizationPlugin.h +++ b/plugin/groupNormalizationPlugin/groupNormalizationPlugin.h @@ -17,12 +17,12 @@ #ifndef TRT_GROUP_NORM_PLUGIN_H #define TRT_GROUP_NORM_PLUGIN_H -#include "plugin.h" #include "serialize.hpp" +#include "plugin.h" #include +#include #include #include -#include // One of the preferred ways of making TensorRT to be able to see // our custom layer requires extending IPluginV2 and IPluginCreator classes. @@ -33,7 +33,7 @@ namespace plugin { template -void scaleShiftChannelsInplace(T* inOut, const int B, const int C, const int channelVolume, const float* beta, +cudaError_t scaleShiftChannelsInplace(T* inOut, const int B, const int C, const int channelVolume, const float* beta, const float* gamma, cudaStream_t stream); class GroupNormalizationPlugin final : public nvinfer1::IPluginV2DynamicExt @@ -47,49 +47,49 @@ public: // delete default constructor. GroupNormalizationPlugin() = delete; - int getNbOutputs() const override; + int getNbOutputs() const noexcept override; // DynamicExt plugins returns DimsExprs class instead of Dims - DimsExprs getOutputDimensions( - int index, const nvinfer1::DimsExprs* inputs, int nbInputDims, nvinfer1::IExprBuilder& exprBuilder) override; + DimsExprs getOutputDimensions(int index, const nvinfer1::DimsExprs* inputs, int nbInputDims, + nvinfer1::IExprBuilder& exprBuilder) noexcept override; - int initialize() override; + int initialize() noexcept override; - void terminate() override; + void terminate() noexcept override; size_t getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int nbInputs, - const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const override; + const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const noexcept override; int enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) override; + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; - size_t getSerializationSize() const override; + size_t getSerializationSize() const noexcept override; - void serialize(void* buffer) const override; + void serialize(void* buffer) const noexcept override; bool supportsFormatCombination( - int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) override; + int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept override; - const char* getPluginType() const override; + const char* getPluginType() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - nvinfer1::IPluginV2DynamicExt* clone() const override; + nvinfer1::IPluginV2DynamicExt* clone() const noexcept override; - void destroy() override; + void destroy() noexcept override; - DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept override; - void attachToContext(cudnnContext* cudnn, cublasContext* cublas, nvinfer1::IGpuAllocator* allocator) override; + void attachToContext(cudnnContext* cudnn, cublasContext* cublas, nvinfer1::IGpuAllocator* allocator) noexcept override; - void detachFromContext() override; + void detachFromContext() noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, - const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) override; + const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) noexcept override; private: const char* mPluginNamespace; @@ -104,16 +104,6 @@ private: // These are buffers initialized to 1 and 0 respectively void* bnScale; void* bnBias; - -protected: - // To prevent compiler warnings. - using nvinfer1::IPluginV2DynamicExt::getOutputDimensions; - using nvinfer1::IPluginV2DynamicExt::isOutputBroadcastAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::canBroadcastInputAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::supportsFormat; - using nvinfer1::IPluginV2DynamicExt::configurePlugin; - using nvinfer1::IPluginV2DynamicExt::getWorkspaceSize; - using nvinfer1::IPluginV2DynamicExt::enqueue; }; class GroupNormalizationPluginCreator : public IPluginCreator @@ -123,19 +113,19 @@ public: ~GroupNormalizationPluginCreator() override = default; - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const PluginFieldCollection* getFieldNames() override; + const PluginFieldCollection* getFieldNames() noexcept override; - IPluginV2DynamicExt* createPlugin(const char* name, const PluginFieldCollection* fc) override; + IPluginV2DynamicExt* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; - IPluginV2DynamicExt* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + IPluginV2DynamicExt* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; private: static PluginFieldCollection mFC; diff --git a/plugin/instanceNormalizationPlugin/instanceNormCommon.h b/plugin/instanceNormalizationPlugin/instanceNormCommon.h index 6c5592ed..c305acfc 100644 --- a/plugin/instanceNormalizationPlugin/instanceNormCommon.h +++ b/plugin/instanceNormalizationPlugin/instanceNormCommon.h @@ -21,7 +21,7 @@ #define DEVICE_FUNCTION static inline __device__ -template +template struct PackedStorage { enum @@ -31,7 +31,7 @@ struct PackedStorage typedef T Type; }; -template +template struct PackedStorage { enum @@ -41,7 +41,7 @@ struct PackedStorage typedef int32_t Type; }; -template +template struct PackedStorage { enum @@ -51,11 +51,11 @@ struct PackedStorage typedef int32_t Type; }; -template -DEVICE_FUNCTION void from_float(int32_t (&dst)[N], const float (&src)[2 * N]) +template +DEVICE_FUNCTION void fromFloat(int32_t (&dst)[N], const float (&src)[2 * N]) { #pragma unroll - for (int i = 0; i < N; ++i) + for (int32_t i = 0; i < N; ++i) { uint16_t lo, hi; asm volatile("cvt.rn.f16.f32 %0, %1;" : "=h"(lo) : "f"(src[2 * i + 0])); @@ -64,19 +64,19 @@ DEVICE_FUNCTION void from_float(int32_t (&dst)[N], const float (&src)[2 * N]) } } -template -DEVICE_FUNCTION void from_float(int32_t (&dst)[N], const float (&src)[4 * N], float scale) +template +DEVICE_FUNCTION void fromFloat(int32_t (&dst)[N], const float (&src)[4 * N], float scale) { union Pack_t { int8_t x[4]; int32_t val; }; #pragma unroll - for (int i = 0; i < N; ++i) + for (int32_t i = 0; i < N; ++i) { Pack_t packed; #pragma unroll - for (int ii = 0; ii < 4; ii++) + for (int32_t ii = 0; ii < 4; ii++) { packed.x[ii] = __float_as_int(min(max(src[4 * i + ii] * scale + 12582912.0F, 12582785.0F), 12583039.0F)); } @@ -84,21 +84,21 @@ DEVICE_FUNCTION void from_float(int32_t (&dst)[N], const float (&src)[4 * N], fl } } -template -DEVICE_FUNCTION void from_float(float (&dst)[N], const float (&src)[N]) +template +DEVICE_FUNCTION void fromFloat(float (&dst)[N], const float (&src)[N]) { #pragma unroll - for (int i = 0; i < N; ++i) + for (int32_t i = 0; i < N; ++i) { dst[i] = src[i]; } } -template -DEVICE_FUNCTION void to_float(float (&dst)[2 * N], int32_t (&src)[N], float scale = 1.f) +template +DEVICE_FUNCTION void toFloat(float (&dst)[2 * N], int32_t (&src)[N], float scale = 1.f) { #pragma unroll - for (int i = 0; i < N; ++i) + for (int32_t i = 0; i < N; ++i) { uint16_t lo, hi; asm volatile("mov.b32 {%0, %1}, %2;" : "=h"(lo), "=h"(hi) : "r"(src[i])); @@ -107,20 +107,20 @@ DEVICE_FUNCTION void to_float(float (&dst)[2 * N], int32_t (&src)[N], float scal } } -template -DEVICE_FUNCTION void to_float(float (&dst)[4 * N], int32_t (&src)[N], float scale = 1.f) +template +DEVICE_FUNCTION void toFloat(float (&dst)[4 * N], int32_t (&src)[N], float scale = 1.f) { union Pack_t { int8_t x[4]; int32_t val; }; #pragma unroll - for (int i = 0; i < N; ++i) + for (int32_t i = 0; i < N; ++i) { Pack_t packed; packed.val = src[i]; #pragma unroll - for (int ii = 0; ii < 4; ++ii) + for (int32_t ii = 0; ii < 4; ++ii) { dst[4 * i + ii] = (DO_SCALE) ? __int2float_rn((int32_t) packed.x[ii]) * scale : __int2float_rn((int32_t) packed.x[ii]); @@ -128,26 +128,26 @@ DEVICE_FUNCTION void to_float(float (&dst)[4 * N], int32_t (&src)[N], float scal } } -template -DEVICE_FUNCTION void to_float(float (&dst)[N], float (&src)[N], float scale = 1.f) +template +DEVICE_FUNCTION void toFloat(float (&dst)[N], float (&src)[N], float scale = 1.f) { #pragma unroll - for (int i = 0; i < N; ++i) + for (int32_t i = 0; i < N; ++i) { dst[i] = (DO_SCALE) ? src[i] * scale : src[i]; } } template -DEVICE_FUNCTION void ldg(int (&dst)[1], const T* gmem) +DEVICE_FUNCTION void ldg(int32_t (&dst)[1], const T* gmem) { - dst[0] = __ldg((const int*) gmem); + dst[0] = __ldg((const int32_t*) gmem); } template -DEVICE_FUNCTION void ldg_stream(int (&dst)[1], const T* gmem) +DEVICE_FUNCTION void ldgStream(int32_t (&dst)[1], const T* gmem) { - unsigned int tmp; + uint32_t tmp; asm volatile("ld.global.cs.nc.s32 %0, [%1];" : "=r"(tmp) : "l"((const uint32_t*) gmem)); dst[0] = tmp; } @@ -161,7 +161,7 @@ DEVICE_FUNCTION void ldg(int32_t (&dst)[2], const T* gmem) } template -DEVICE_FUNCTION void ldg_stream(int32_t (&dst)[2], const T* gmem) +DEVICE_FUNCTION void ldgStream(int32_t (&dst)[2], const T* gmem) { int2 tmp; asm volatile("ld.global.cs.nc.v2.s32 {%0,%1}, [%2];" : "=r"(tmp.x), "=r"(tmp.y) : "l"((const int2*) gmem)); @@ -178,7 +178,7 @@ DEVICE_FUNCTION void ldg(int32_t (&dst)[2], const uint16_t* gmem) #endif } -DEVICE_FUNCTION void ldg_stream(int32_t (&dst)[2], const uint16_t* gmem) +DEVICE_FUNCTION void ldgStream(int32_t (&dst)[2], const uint16_t* gmem) { int2 tmp; asm volatile("ld.global.cs.nc.v2.s32 {%0,%1}, [%2];" : "=r"(tmp.x), "=r"(tmp.y) : "l"((const int2*) gmem)); @@ -186,36 +186,36 @@ DEVICE_FUNCTION void ldg_stream(int32_t (&dst)[2], const uint16_t* gmem) dst[1] = tmp.y; } -template +template DEVICE_FUNCTION void ldg(float (&dst)[N], const uint16_t* gmem) { int32_t tmp[N / 2]; ldg(tmp, gmem); - to_float(dst, tmp); + toFloat(dst, tmp); } -template -DEVICE_FUNCTION void ldg_stream(float (&dst)[N], const uint16_t* gmem) +template +DEVICE_FUNCTION void ldgStream(float (&dst)[N], const uint16_t* gmem) { int32_t tmp[N / 2]; - ldg_stream(tmp, gmem); - to_float(dst, tmp); + ldgStream(tmp, gmem); + toFloat(dst, tmp); } -template +template DEVICE_FUNCTION void ldg(float (&dst)[N], const int8_t* gmem) { int32_t tmp[N / 4]; ldg(tmp, gmem); - to_float(dst, tmp); + toFloat(dst, tmp); } -template -DEVICE_FUNCTION void ldg_stream(float (&dst)[N], const int8_t* gmem) +template +DEVICE_FUNCTION void ldgStream(float (&dst)[N], const int8_t* gmem) { int32_t tmp[N / 4]; - ldg_stream(tmp, gmem); - to_float(dst, tmp); + ldgStream(tmp, gmem); + toFloat(dst, tmp); } template @@ -225,7 +225,7 @@ DEVICE_FUNCTION void stg(T* gmem, int32_t (&src)[1]) } template -DEVICE_FUNCTION void stg_stream(T* gmem, int32_t (&src)[1]) +DEVICE_FUNCTION void stgStream(T* gmem, int32_t (&src)[1]) { uint32_t tmp = src[0]; asm volatile("st.global.cs.s32 [%0], %1;" ::"l"((uint32_t*) gmem), "r"(tmp)); @@ -238,51 +238,51 @@ DEVICE_FUNCTION void stg(T* gmem, int32_t (&src)[2]) } template -DEVICE_FUNCTION void stg_stream(T* gmem, int32_t (&src)[2]) +DEVICE_FUNCTION void stgStream(T* gmem, int32_t (&src)[2]) { asm volatile("st.global.cs.v2.s32 [%0], {%1,%2};" ::"l"((uint32_t*) gmem), "r"(src[0]), "r"(src[1])); } -template +template DEVICE_FUNCTION void stg(uint16_t* gmem, float (&src)[N], float scale) { int32_t tmp[N / 2]; - from_float(tmp, src); + fromFloat(tmp, src); stg(gmem, tmp); } -template -DEVICE_FUNCTION void stg_stream(uint16_t* gmem, float (&src)[N], float scale) +template +DEVICE_FUNCTION void stgStream(uint16_t* gmem, float (&src)[N], float scale) { int32_t tmp[N / 2]; - from_float(tmp, src); - stg_stream(gmem, tmp); + fromFloat(tmp, src); + stgStream(gmem, tmp); } -template +template DEVICE_FUNCTION void stg(int8_t* gmem, float (&src)[N], float scale) { int32_t tmp[N / 4]; - from_float(tmp, src, scale); + fromFloat(tmp, src, scale); stg(gmem, tmp); } -template -DEVICE_FUNCTION void stg_stream(int8_t* gmem, float (&src)[N], float scale) +template +DEVICE_FUNCTION void stgStream(int8_t* gmem, float (&src)[N], float scale) { int32_t tmp[N / 4]; - from_float(tmp, src, scale); + fromFloat(tmp, src, scale); stg(gmem, tmp); } -DEVICE_FUNCTION void read_from_gmem(float (&dst)[2], const float* gmem, int idx) +DEVICE_FUNCTION void readFromGmem(float (&dst)[2], const float* gmem, int32_t idx) { float2 tmp = __ldg((float2*) &gmem[2 * idx]); dst[0] = tmp.x; dst[1] = tmp.y; } -DEVICE_FUNCTION void read_from_gmem(float (&dst)[4], const float* gmem, int idx) +DEVICE_FUNCTION void readFromGmem(float (&dst)[4], const float* gmem, int32_t idx) { float4 tmp = __ldg((float4*) &gmem[4 * idx]); dst[0] = tmp.x; @@ -291,8 +291,8 @@ DEVICE_FUNCTION void read_from_gmem(float (&dst)[4], const float* gmem, int idx) dst[3] = tmp.w; } -template -DEVICE_FUNCTION void read_from_gmem(float (&dst)[N], const __half* gmem, int idx) +template +DEVICE_FUNCTION void readFromGmem(float (&dst)[N], const __half* gmem, int32_t idx) { int32_t ival[N / 2]; if (N == 4) @@ -300,7 +300,7 @@ DEVICE_FUNCTION void read_from_gmem(float (&dst)[N], const __half* gmem, int idx else reinterpret_cast(ival)[0] = __ldg((int32_t*) &gmem[2 * idx]); #pragma unroll - for (int i = 0; i < N / 2; ++i) + for (int32_t i = 0; i < N / 2; ++i) { uint16_t lo, hi; asm volatile("mov.b32 {%0, %1}, %2;" : "=h"(lo), "=h"(hi) : "r"(ival[i])); @@ -309,14 +309,14 @@ DEVICE_FUNCTION void read_from_gmem(float (&dst)[N], const __half* gmem, int idx } } -DEVICE_FUNCTION void read_from_smem(float (&x)[2], const float* smem, int idx) +DEVICE_FUNCTION void readFromSmem(float (&x)[2], const float* smem, int32_t idx) { float2 tmp = *(const float2*) &smem[2 * idx]; x[0] = tmp.x; x[1] = tmp.y; } -DEVICE_FUNCTION void read_from_smem(float (&x)[4], const float* smem, int idx) +DEVICE_FUNCTION void readFromSmem(float (&x)[4], const float* smem, int32_t idx) { float4 tmp = *(const float4*) &smem[4 * idx]; x[0] = tmp.x; @@ -325,34 +325,34 @@ DEVICE_FUNCTION void read_from_smem(float (&x)[4], const float* smem, int idx) x[3] = tmp.w; } -DEVICE_FUNCTION void read_from_smem(int32_t (&x)[1], const int32_t* smem, int idx) +DEVICE_FUNCTION void readFromSmem(int32_t (&x)[1], const int32_t* smem, int32_t idx) { x[0] = smem[idx]; } -DEVICE_FUNCTION void read_from_smem(int32_t (&x)[2], const int32_t* smem, int idx) +DEVICE_FUNCTION void readFromSmem(int32_t (&x)[2], const int32_t* smem, int32_t idx) { int2 tmp = *(const int2*) &smem[2 * idx]; x[0] = tmp.x; x[1] = tmp.y; } -DEVICE_FUNCTION void write_to_gmem(float* gmem, int idx, const float (&src)[2]) +DEVICE_FUNCTION void writeToGmem(float* gmem, int32_t idx, const float (&src)[2]) { reinterpret_cast(&gmem[2 * idx])[0] = make_float2(src[0], src[1]); } -DEVICE_FUNCTION void write_to_gmem(float* gmem, int idx, const float (&src)[4]) +DEVICE_FUNCTION void writeToGmem(float* gmem, int32_t idx, const float (&src)[4]) { reinterpret_cast(&gmem[4 * idx])[0] = make_float4(src[0], src[1], src[2], src[3]); } -template -DEVICE_FUNCTION void write_to_gmem(__half* gmem, int idx, const float (&src)[N]) +template +DEVICE_FUNCTION void writeToGmem(__half* gmem, int32_t idx, const float (&src)[N]) { int32_t ival[N / 2]; #pragma unroll - for (int i = 0; i < N / 2; ++i) + for (int32_t i = 0; i < N / 2; ++i) { uint16_t lo; uint16_t hi; @@ -370,61 +370,61 @@ DEVICE_FUNCTION void write_to_gmem(__half* gmem, int idx, const float (&src)[N]) } } -DEVICE_FUNCTION void write_to_smem(float* smem, int idx, const float (&x)[2]) +DEVICE_FUNCTION void writeToSmem(float* smem, int32_t idx, const float (&x)[2]) { reinterpret_cast(&smem[2 * idx])[0] = make_float2(x[0], x[1]); } -DEVICE_FUNCTION void write_to_smem(float* smem, int idx, const float (&x)[4]) +DEVICE_FUNCTION void writeToSmem(float* smem, int32_t idx, const float (&x)[4]) { reinterpret_cast(&smem[4 * idx])[0] = make_float4(x[0], x[1], x[2], x[3]); } -DEVICE_FUNCTION void write_to_smem(int32_t* smem, int idx, const int (&x)[1]) +DEVICE_FUNCTION void writeToSmem(int32_t* smem, int32_t idx, const int32_t (&x)[1]) { smem[idx] = x[0]; } -static inline __device__ void write_to_smem(int32_t* smem, int idx, const int (&x)[2]) +static inline __device__ void writeToSmem(int32_t* smem, int32_t idx, const int32_t (&x)[2]) { reinterpret_cast(&smem[2 * idx])[0] = make_int2(x[0], x[1]); } -template +template DEVICE_FUNCTION void zero(int32_t (&dst)[N]) { #pragma unroll - for (int i = 0; i < N; ++i) + for (int32_t i = 0; i < N; ++i) { dst[i] = 0; } } -template +template DEVICE_FUNCTION void zero(float (&dst)[N]) { #pragma unroll - for (int i = 0; i < N; ++i) + for (int32_t i = 0; i < N; ++i) { dst[i] = 0.f; } } -template +template DEVICE_FUNCTION void add(float (&x)[N], const float (&y)[N]) { #pragma unroll - for (int i = 0; i < N; ++i) + for (int32_t i = 0; i < N; ++i) { x[i] += y[i]; } } -template +template DEVICE_FUNCTION void normalize(float (&x)[N], const float (&bias)[N], const float (&scale)[N], const float (&m1)[N]) { #pragma unroll - for (int i = 0; i < N; ++i) + for (int32_t i = 0; i < N; ++i) { x[i] = bias[i] + scale[i] * (x[i] - m1[i]); } @@ -437,34 +437,34 @@ DEVICE_FUNCTION Storage relu(Storage in, Storage alpha) return (in < zero) ? in * alpha : in; } -template -DEVICE_FUNCTION void relu_activation(float (&x)[N], float alpha) +template +DEVICE_FUNCTION void reluActivation(float (&x)[N], float alpha) { #pragma unroll - for (int i = 0; i < N; ++i) + for (int32_t i = 0; i < N; ++i) { x[i] = relu(x[i], alpha); } } -template -DEVICE_FUNCTION void parallel_sums_16x2(float* smem, float (&x)[4], int nhw) +template +DEVICE_FUNCTION void parallelSums_16x2(float* smem, float (&x)[4], int32_t nhw) { // The size of a warp. - const int THREADS_PER_WARP = 32; + const int32_t THREADS_PER_WARP = 32; // The number of warps in a CTA. - const int WARPS_PER_CTA = THREADS_PER_CTA / THREADS_PER_WARP; + const int32_t WARPS_PER_CTA = THREADS_PER_CTA / THREADS_PER_WARP; // The number of threads per pixel. - const int THREADS_PER_PIXEL = 16; + const int32_t THREADS_PER_PIXEL = 16; // The number of elements per ldg. - const int ELEMENTS_PER_LDG = 4; + const int32_t ELEMENTS_PER_LDG = 4; // The warp decomposition. - const int warp_id = threadIdx.x / THREADS_PER_WARP; - const int lane_id = threadIdx.x % THREADS_PER_WARP; + const int32_t warp_id = threadIdx.x / THREADS_PER_WARP; + const int32_t lane_id = threadIdx.x % THREADS_PER_WARP; // Store the values to shared memory. - write_to_smem(smem, threadIdx.x, x); + writeToSmem(smem, threadIdx.x, x); // Compute the parallel sum inside the warp. Use SHFL and reduce the amount of SMEM by 2x? __syncwarp(); @@ -473,7 +473,7 @@ DEVICE_FUNCTION void parallel_sums_16x2(float* smem, float (&x)[4], int nhw) float y[ELEMENTS_PER_LDG]; if (lane_id < THREADS_PER_PIXEL) { - read_from_smem(y, smem, threadIdx.x + THREADS_PER_PIXEL); + readFromSmem(y, smem, threadIdx.x + THREADS_PER_PIXEL); } // Compute the updated sum. @@ -485,7 +485,7 @@ DEVICE_FUNCTION void parallel_sums_16x2(float* smem, float (&x)[4], int nhw) // The warp leaders, write to SMEM. if (lane_id < THREADS_PER_PIXEL) { - write_to_smem(smem, warp_id * THREADS_PER_PIXEL + lane_id, x); + writeToSmem(smem, warp_id * THREADS_PER_PIXEL + lane_id, x); } // The data is in SMEM. Do the final reduction. @@ -494,18 +494,18 @@ DEVICE_FUNCTION void parallel_sums_16x2(float* smem, float (&x)[4], int nhw) // The 1st warp does all the work. if (warp_id == 0) { - read_from_smem(x, smem, threadIdx.x); + readFromSmem(x, smem, threadIdx.x); } // We do the final reduction each half-warp sequentially reduces the final values. #pragma unroll - for (int offset = 1; offset < WARPS_PER_CTA / 2; ++offset) + for (int32_t offset = 1; offset < WARPS_PER_CTA / 2; ++offset) { // Read the mean and variance from the other pixel. if (warp_id == 0) { - read_from_smem(y, smem, threadIdx.x + offset * THREADS_PER_WARP); + readFromSmem(y, smem, threadIdx.x + offset * THREADS_PER_WARP); } // Compute the updated sum. @@ -518,7 +518,7 @@ DEVICE_FUNCTION void parallel_sums_16x2(float* smem, float (&x)[4], int nhw) // Store the mean/var for the different pixels. TODO: Use SHFL? if (warp_id == 0) { - write_to_smem(smem, threadIdx.x, x); + writeToSmem(smem, threadIdx.x, x); } // Make sure the data is in SMEM. @@ -527,7 +527,7 @@ DEVICE_FUNCTION void parallel_sums_16x2(float* smem, float (&x)[4], int nhw) // The first half warp finishes the work. if (threadIdx.x < THREADS_PER_PIXEL) { - read_from_smem(y, smem, threadIdx.x + THREADS_PER_PIXEL); + readFromSmem(y, smem, threadIdx.x + THREADS_PER_PIXEL); } // Compute the updated sum. @@ -539,27 +539,27 @@ DEVICE_FUNCTION void parallel_sums_16x2(float* smem, float (&x)[4], int nhw) // Store the final values. if (threadIdx.x < THREADS_PER_PIXEL) { - write_to_smem(smem, threadIdx.x, x); + writeToSmem(smem, threadIdx.x, x); } } -template -static inline __device__ void parallel_sums_8x4(float* smem, float (&x)[4], int nhw) +template +static inline __device__ void parallelSums_8x4(float* smem, float (&x)[4], int32_t nhw) { // The size of a warp. - const int THREADS_PER_WARP = 32; + const int32_t THREADS_PER_WARP = 32; // The number of warps in a CTA. - const int WARPS_PER_CTA = THREADS_PER_CTA / THREADS_PER_WARP; + const int32_t WARPS_PER_CTA = THREADS_PER_CTA / THREADS_PER_WARP; // The number of threads per pixel. - const int THREADS_PER_PIXEL = 8; + const int32_t THREADS_PER_PIXEL = 8; // The number of elements per ldg. - const int ELEMENTS_PER_LDG = 4; + const int32_t ELEMENTS_PER_LDG = 4; // The warp decomposition. - const int warp_id = threadIdx.x / THREADS_PER_WARP; - const int lane_id = threadIdx.x % THREADS_PER_WARP; + const int32_t warp_id = threadIdx.x / THREADS_PER_WARP; + const int32_t lane_id = threadIdx.x % THREADS_PER_WARP; #pragma unroll - for (int i = 0; i < ELEMENTS_PER_LDG; ++i) + for (int32_t i = 0; i < ELEMENTS_PER_LDG; ++i) { x[i] += __shfl_sync(0xffffffffU, x[i], THREADS_PER_PIXEL + lane_id); x[i] += __shfl_sync(0xffffffffU, x[i], THREADS_PER_PIXEL * 2 + lane_id); @@ -568,7 +568,7 @@ static inline __device__ void parallel_sums_8x4(float* smem, float (&x)[4], int // The warp leaders, write to SMEM. if (lane_id < THREADS_PER_PIXEL) { - write_to_smem(smem, warp_id * THREADS_PER_PIXEL + lane_id, x); + writeToSmem(smem, warp_id * THREADS_PER_PIXEL + lane_id, x); } // The data is in SMEM. Do the final reduction. @@ -578,19 +578,19 @@ static inline __device__ void parallel_sums_8x4(float* smem, float (&x)[4], int // We do the final reduction each half-warp sequentially reduces the final values. if (warp_id == 0) { - read_from_smem(x, smem, threadIdx.x); + readFromSmem(x, smem, threadIdx.x); #pragma unroll - for (int offset = 1; offset < WARPS_PER_CTA / (THREADS_PER_WARP / THREADS_PER_PIXEL); ++offset) + for (int32_t offset = 1; offset < WARPS_PER_CTA / (THREADS_PER_WARP / THREADS_PER_PIXEL); ++offset) { float y[ELEMENTS_PER_LDG]; // Read the mean and variance from the other pixel. - read_from_smem(y, smem, threadIdx.x + offset * THREADS_PER_WARP); + readFromSmem(y, smem, threadIdx.x + offset * THREADS_PER_WARP); // Compute the updated sum. add(x, y); } - for (int i = 0; i < ELEMENTS_PER_LDG; ++i) + for (int32_t i = 0; i < ELEMENTS_PER_LDG; ++i) { x[i] += __shfl_sync(0xffffffffU, x[i], THREADS_PER_PIXEL + lane_id); x[i] += __shfl_sync(0xffffffffU, x[i], THREADS_PER_PIXEL * 2 + lane_id); @@ -602,32 +602,32 @@ static inline __device__ void parallel_sums_8x4(float* smem, float (&x)[4], int // Store the final values. if (threadIdx.x < THREADS_PER_PIXEL) { - write_to_smem(smem, threadIdx.x, x); + writeToSmem(smem, threadIdx.x, x); } } } -template -DEVICE_FUNCTION void parallel_sums(float* smem, float (&x)[ELEMENTS_PER_LDG], int nhw) +template +DEVICE_FUNCTION void parallelSums(float* smem, float (&x)[ELEMENTS_PER_LDG], int32_t nhw) { // The size of a warp. - const int THREADS_PER_WARP = 32; + const int32_t THREADS_PER_WARP = 32; // The number of warps in a CTA. - const int WARPS_PER_CTA = THREADS_PER_CTA / THREADS_PER_WARP; + const int32_t WARPS_PER_CTA = THREADS_PER_CTA / THREADS_PER_WARP; // The number of pixels computed by a single warp. - const int PIXELS_PER_WARP = THREADS_PER_WARP / THREADS_PER_PIXEL; + const int32_t PIXELS_PER_WARP = THREADS_PER_WARP / THREADS_PER_PIXEL; // The position in the warp. - const int nhw_in_warp = nhw % PIXELS_PER_WARP; + const int32_t nhw_in_warp = nhw % PIXELS_PER_WARP; // The C in the warp. - const int c_in_warp = threadIdx.x % THREADS_PER_PIXEL; + const int32_t c_in_warp = threadIdx.x % THREADS_PER_PIXEL; // Store the values to shared memory. - write_to_smem(smem, threadIdx.x, x); + writeToSmem(smem, threadIdx.x, x); // Compute the parallel sums. - for (int offset = PIXELS_PER_WARP / 2; offset > 0; offset /= 2) + for (int32_t offset = PIXELS_PER_WARP / 2; offset > 0; offset /= 2) { if ((WARPS_PER_CTA * THREADS_PER_WARP) / THREADS_PER_PIXEL > THREADS_PER_WARP) @@ -644,7 +644,7 @@ DEVICE_FUNCTION void parallel_sums(float* smem, float (&x)[ELEMENTS_PER_LDG], in float y[ELEMENTS_PER_LDG]; if (nhw_in_warp < offset) { - read_from_smem(y, smem, threadIdx.x + offset * THREADS_PER_PIXEL); + readFromSmem(y, smem, threadIdx.x + offset * THREADS_PER_PIXEL); } // Compute the updated sum. @@ -663,7 +663,7 @@ DEVICE_FUNCTION void parallel_sums(float* smem, float (&x)[ELEMENTS_PER_LDG], in // Update the sum in SMEM. if (offset > 1 && nhw_in_warp < offset) { - write_to_smem(smem, threadIdx.x, x); + writeToSmem(smem, threadIdx.x, x); } } @@ -671,10 +671,10 @@ DEVICE_FUNCTION void parallel_sums(float* smem, float (&x)[ELEMENTS_PER_LDG], in __syncthreads(); // The warp leaders, write to SMEM. - const int idx = (threadIdx.x / THREADS_PER_WARP) * THREADS_PER_PIXEL + c_in_warp; + const int32_t idx = (threadIdx.x / THREADS_PER_WARP) * THREADS_PER_PIXEL + c_in_warp; if (nhw_in_warp == 0) { - write_to_smem(smem, idx, x); + writeToSmem(smem, idx, x); } // The data is in SMEM. Do the final reduction. @@ -683,11 +683,11 @@ DEVICE_FUNCTION void parallel_sums(float* smem, float (&x)[ELEMENTS_PER_LDG], in // Read the 1st element to prepare the work. if (nhw < WARPS_PER_CTA / 2) { - read_from_smem(x, smem, threadIdx.x); + readFromSmem(x, smem, threadIdx.x); } // We have the running mean and running m2. Let's build the mean/var of the CTA. - for (int offset = WARPS_PER_CTA / 2; offset > 0; offset /= 2) + for (int32_t offset = WARPS_PER_CTA / 2; offset > 0; offset /= 2) { if ((WARPS_PER_CTA * THREADS_PER_WARP) / THREADS_PER_PIXEL > THREADS_PER_WARP) @@ -704,7 +704,7 @@ DEVICE_FUNCTION void parallel_sums(float* smem, float (&x)[ELEMENTS_PER_LDG], in float y[ELEMENTS_PER_LDG]; if (nhw < offset) { - read_from_smem(y, smem, threadIdx.x + offset * THREADS_PER_PIXEL); + readFromSmem(y, smem, threadIdx.x + offset * THREADS_PER_PIXEL); } // Compute the updated sum. @@ -723,38 +723,38 @@ DEVICE_FUNCTION void parallel_sums(float* smem, float (&x)[ELEMENTS_PER_LDG], in // Store the mean/var for the different pixels. if (nhw < offset) { - write_to_smem(smem, threadIdx.x, x); + writeToSmem(smem, threadIdx.x, x); } } } -template +template struct ParallelSums { - template - DEVICE_FUNCTION void dispatch(float* smem, float (&x)[ELEMENTS_PER_LDG], int nhw) + template + DEVICE_FUNCTION void dispatch(float* smem, float (&x)[ELEMENTS_PER_LDG], int32_t nhw) { - parallel_sums(smem, x, nhw); + parallelSums(smem, x, nhw); } }; template <> struct ParallelSums<16, 4> { - template - DEVICE_FUNCTION void dispatch(float* smem, float (&x)[4], int nhw) + template + DEVICE_FUNCTION void dispatch(float* smem, float (&x)[4], int32_t nhw) { - parallel_sums_16x2(smem, x, nhw); + parallelSums_16x2(smem, x, nhw); } }; template <> struct ParallelSums<8, 4> { - template - static inline __device__ void dispatch(float* smem, float (&x)[4], int nhw) + template + static inline __device__ void dispatch(float* smem, float (&x)[4], int32_t nhw) { - parallel_sums_8x4(smem, x, nhw); + parallelSums_8x4(smem, x, nhw); } }; diff --git a/plugin/instanceNormalizationPlugin/instanceNormFwd.h b/plugin/instanceNormalizationPlugin/instanceNormFwd.h index 93f01417..6bf8df2b 100644 --- a/plugin/instanceNormalizationPlugin/instanceNormFwd.h +++ b/plugin/instanceNormalizationPlugin/instanceNormFwd.h @@ -17,7 +17,7 @@ #ifndef INSTANCE_NORM_FWD_H #define INSTANCE_NORM_FWD_H -#include +#include #include #include @@ -44,32 +44,29 @@ namespace instance_norm_impl } \ } while (0) -// typedef __half GMEM_SUMS_TYPE; typedef float GMEM_SUMS_TYPE; -#define DISABLE_MEAN_VAR_OUTPUT 0 +#define ACCUM_MEAN_VAR_IN_FLOAT 1 - -template -constexpr int get_pixels_per_thread_in_registers() +template +constexpr int32_t getPixelsPerThreadInRegisters() { return (sizeof(StorageType) == 4 || sizeof(StorageType) == 2) ? 6 - sizeof(StorageType) : (SM < 800 ? (SM == 750 ? 16 : 8) : (SM == 860 ? 16 : 24)); } - -template -constexpr int get_pixels_per_thread_in_smem() +template +constexpr int32_t getPixelsPerThreadInSmem() { return (sizeof(StorageType) == 4 || sizeof(StorageType) == 2) ? (sizeof(StorageType) == 4 ? 4 : 8) : (SM < 800 ? (SM == 750 ? 7 : 8) : (SM == 860 ? 16 : 24)); } - template + int32_t THREADS_PER_CTA_ = 512, int32_t THREADS_PER_PIXEL_ = 16, int32_t C_ELEMENTS_PER_CTA_ = 64, + int32_t SM_ = 700> struct Instance_norm_kernel_params { enum @@ -95,11 +92,11 @@ struct Instance_norm_kernel_params typedef StorageType_ StorageType; enum { - PIXELS_PER_THREAD_IN_REGISTERS = get_pixels_per_thread_in_registers() + PIXELS_PER_THREAD_IN_REGISTERS = getPixelsPerThreadInRegisters() }; enum { - PIXELS_PER_THREAD_IN_SMEM = get_pixels_per_thread_in_smem() + PIXELS_PER_THREAD_IN_SMEM = getPixelsPerThreadInSmem() }; enum @@ -122,16 +119,15 @@ struct Instance_norm_kernel_params }; }; - struct InstanceNormFwdContext { InstanceNormFwdContext() : sm_count(0) , sm_shared_size(0) , sm_version(0){}; - int sm_count; - int sm_shared_size; - int sm_version; + int32_t sm_count; + int32_t sm_shared_size; + int32_t sm_version; }; struct InstanceNormFwdParams @@ -149,37 +145,37 @@ struct InstanceNormFwdParams float* gmem_saved_mean; float* gmem_saved_var; // The dimensions. - int nhw; - int c; - int n; + int32_t nhw; + int32_t c; + int32_t n; // The buffer to do the reduction for mean, stddev and count. GMEM_SUMS_TYPE* gmem_sums; // The buffer to count items in the different CTAs. - int* gmem_counts; + int32_t* gmem_counts; // The counters of retired CTAs. - int* gmem_retired_ctas; + int32_t* gmem_retired_ctas; // The epsilon to apply to the computation of the variance. float var_eps; // outer loop count - int outer_loops; + int32_t outer_loops; // exponential average factor float exp_avg_factor; bool use_relu; float relu_alpha; - int c_blks; + int32_t c_blks; float in_scale; float out_scale; }; -void instance_norm_buffer_sizes_dispatch(const InstanceNormFwdContext& context, const InstanceNormFwdParams& params, - size_t& size_sums, size_t& size_counts, size_t& size_retired_ctas, int input_data_type = 1, - int output_data_type = 1); +void instanceNormBufferSizesDispatch(const InstanceNormFwdContext& context, const InstanceNormFwdParams& params, + size_t& size_sums, size_t& size_counts, size_t& size_retired_ctas, int32_t input_data_type = 1, + int32_t output_data_type = 1); -int instance_norm_fwd_dispatch(const InstanceNormFwdContext& context, InstanceNormFwdParams& params, - cudaStream_t stream, int input_data_type = 1, int output_data_type = 1); +int32_t instanceNormFwdDispatch(const InstanceNormFwdContext& context, InstanceNormFwdParams& params, + cudaStream_t stream, int32_t input_data_type = 1, int32_t output_data_type = 1); } // namespace instance_norm_impl diff --git a/plugin/instanceNormalizationPlugin/instanceNormFwdImpl.cu b/plugin/instanceNormalizationPlugin/instanceNormFwdImpl.cu index d0e9861d..feb0c2f4 100644 --- a/plugin/instanceNormalizationPlugin/instanceNormFwdImpl.cu +++ b/plugin/instanceNormalizationPlugin/instanceNormFwdImpl.cu @@ -24,7 +24,7 @@ namespace instance_norm_impl { - static inline int div_up(int m, int n) { + static inline int32_t divUp(int32_t m, int32_t n) { return (m + n - 1) / n; } @@ -41,23 +41,22 @@ // debug : //using kernel_params_32_int8 = Instance_norm_kernel_params; using kernel_params_32_fp16_int8 = Instance_norm_kernel_params; - //using kernel_params_32_int8 = Instance_norm_kernel_params; - + template< typename Storage, typename Input_Data_Type, typename Output_Data_Type, - int THREADS_PER_CTA, - int THREADS_PER_PIXEL, - int PIXELS_PER_THREAD_IN_REGISTERS, - int PIXELS_PER_THREAD_IN_SMEM, - int ELEMENTS_PER_LDG, - int USE_ONLINE_APPROACH, - int OUTER_LOOPS_, - int DESIRED_OCCUPANCY + int32_t THREADS_PER_CTA, + int32_t THREADS_PER_PIXEL, + int32_t PIXELS_PER_THREAD_IN_REGISTERS, + int32_t PIXELS_PER_THREAD_IN_SMEM, + int32_t ELEMENTS_PER_LDG, + int32_t USE_ONLINE_APPROACH, + int32_t OUTER_LOOPS_, + int32_t DESIRED_OCCUPANCY > __global__ __launch_bounds__(THREADS_PER_CTA, DESIRED_OCCUPANCY) - void instance_norm_fwd(InstanceNormFwdParams params) { + void instanceNormFwd(InstanceNormFwdParams params) { // Single pass numerically stable algorithm, see: // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm @@ -80,45 +79,45 @@ const bool IS_OUTPUT_INT8 = std::is_same::value; // The number of pixels loaded in a single LDG. - const int PIXELS_PER_LDG = THREADS_PER_CTA / THREADS_PER_PIXEL; + const int32_t PIXELS_PER_LDG = THREADS_PER_CTA / THREADS_PER_PIXEL; // The number of pixels computed per CTA stored in registers. - const int PIXELS_PER_CTA_IN_REGISTERS = PIXELS_PER_THREAD_IN_REGISTERS * PIXELS_PER_LDG; + const int32_t PIXELS_PER_CTA_IN_REGISTERS = PIXELS_PER_THREAD_IN_REGISTERS * PIXELS_PER_LDG; // The number of pixels computed per CTA stored in SMEM. - const int PIXELS_PER_CTA_IN_SMEM = PIXELS_PER_THREAD_IN_SMEM*PIXELS_PER_LDG; + const int32_t PIXELS_PER_CTA_IN_SMEM = PIXELS_PER_THREAD_IN_SMEM*PIXELS_PER_LDG; // The number of C elements per CTA. - const int C_ELEMENTS_PER_CTA = THREADS_PER_PIXEL*ELEMENTS_PER_LDG; + const int32_t C_ELEMENTS_PER_CTA = THREADS_PER_PIXEL*ELEMENTS_PER_LDG; // Shared memory to do CTA-wide parallel sums. __shared__ float smem[ELEMENTS_PER_LDG*THREADS_PER_CTA]; // The position in the NHW dimension where the CTA starts. - int cta_nhw_regs = blockIdx.x * PIXELS_PER_CTA_IN_REGISTERS; + int32_t cta_nhw_regs = blockIdx.x * PIXELS_PER_CTA_IN_REGISTERS; // The position in the NHW dimension where the CTA starts for the portion in SMEM. - int cta_nhw_smem = blockIdx.x * PIXELS_PER_CTA_IN_SMEM; + int32_t cta_nhw_smem = blockIdx.x * PIXELS_PER_CTA_IN_SMEM; // Compute the NHW coordinate of the thread in the CTA. - const int thread_in_cta_nhw = threadIdx.x / THREADS_PER_PIXEL; + const int32_t thread_in_cta_nhw = threadIdx.x / THREADS_PER_PIXEL; - for (int nc_blk_index = blockIdx.y; nc_blk_index < params.c_blks * params.n; nc_blk_index += gridDim.y) { + for (int32_t nc_blk_index = blockIdx.y; nc_blk_index < params.c_blks * params.n; nc_blk_index += gridDim.y) { - int n_blk_index = nc_blk_index / params.c_blks; - int c_blk_index = nc_blk_index % params.c_blks; + int32_t n_blk_index = nc_blk_index / params.c_blks; + int32_t c_blk_index = nc_blk_index % params.c_blks; // The position in the C dimension where the CTA starts. - const int cta_c = c_blk_index * C_ELEMENTS_PER_CTA; + const int32_t cta_c = c_blk_index * C_ELEMENTS_PER_CTA; // Compute the C coordinate of the thread in the CTA. - const int thread_in_cta_c = threadIdx.x % THREADS_PER_PIXEL; + const int32_t thread_in_cta_c = threadIdx.x % THREADS_PER_PIXEL; // Compute the C coordinate of the thread. - const int thread_c = cta_c + thread_in_cta_c*ELEMENTS_PER_LDG; + const int32_t thread_c = cta_c + thread_in_cta_c*ELEMENTS_PER_LDG; // Is the thread working on a valid C dimension? - const int is_valid_c = thread_c < params.c; + const int32_t is_valid_c = thread_c < params.c; // The adapter for the storage. typedef PackedStorage PackedStorage_; // The data type for packed storage in SMEM. typedef typename PackedStorage_::Type PackedStorageType; // The number of elements in the packed storage. - const int PACKED_ELEMENTS_PER_LDG = PackedStorage_::PACKED_ELEMENTS_PER_LDG; + const int32_t PACKED_ELEMENTS_PER_LDG = PackedStorage_::PACKED_ELEMENTS_PER_LDG; // Registers to keep the data live for the persistent approach. PackedStorageType x_storage[PIXELS_PER_THREAD_IN_REGISTERS][PACKED_ELEMENTS_PER_LDG]; @@ -132,37 +131,37 @@ // Register to store the number of elements read so far. float count = 0.f, mean[ELEMENTS_PER_LDG], m2[ELEMENTS_PER_LDG]; #pragma unroll - for( int i = 0; i < ELEMENTS_PER_LDG; ++i ) { + for( int32_t i = 0; i < ELEMENTS_PER_LDG; ++i ) { mean[i] = 0.f; m2 [i] = 0.f; } // The number of elements loaded by this CTA. - int cta_count = 0; - int global_batch_offset = n_blk_index * params.nhw * params.c; + int32_t cta_count = 0; + int32_t global_batch_offset = n_blk_index * params.nhw * params.c; // int8 relevant // int8 output implies we have NC/32DHW32 input for bath fp16 and int8 - int global_thread_c_input = ( IS_INPUT_INT8 || IS_OUTPUT_INT8 )? thread_in_cta_c*ELEMENTS_PER_LDG + int32_t global_thread_c_input = ( IS_INPUT_INT8 || IS_OUTPUT_INT8 )? thread_in_cta_c*ELEMENTS_PER_LDG + (cta_c % 32) // handle C_ELEMENTS_PER_CTA == 16 case + (cta_c / 32) * 32 * params.nhw : thread_c; - int stride_c_input = ( IS_INPUT_INT8 || IS_OUTPUT_INT8 )? 32 : params.c; - int global_thread_c_output = ( IS_OUTPUT_INT8 )? thread_in_cta_c*ELEMENTS_PER_LDG + int32_t stride_c_input = ( IS_INPUT_INT8 || IS_OUTPUT_INT8 )? 32 : params.c; + int32_t global_thread_c_output = ( IS_OUTPUT_INT8 )? thread_in_cta_c*ELEMENTS_PER_LDG + (cta_c % 32) // handle C_ELEMENTS_PER_CTA == 16 case + (cta_c / 32) * 32 * params.nhw : thread_c; - int stride_c_output = ( IS_OUTPUT_INT8 )? 32 : params.c; + int32_t stride_c_output = ( IS_OUTPUT_INT8 )? 32 : params.c; // The base pointer to load from. const Input_Data_Type *gmem_src = &reinterpret_cast(params.gmem_src)[global_thread_c_input + global_batch_offset]; // Load the batch of elements. Compute the mean/var across those elements. - const int pixels_per_iteration = PIXELS_PER_CTA_IN_REGISTERS*gridDim.x; + const int32_t pixels_per_iteration = PIXELS_PER_CTA_IN_REGISTERS*gridDim.x; // outer loops - int OUTER_LOOPS = OUTER_LOOPS_ == 1? 1 : params.outer_loops; + int32_t OUTER_LOOPS = OUTER_LOOPS_ == 1? 1 : params.outer_loops; #pragma unroll 1 - for( int loop_i = 0; loop_i < OUTER_LOOPS; ++loop_i ) { + for( int32_t loop_i = 0; loop_i < OUTER_LOOPS; ++loop_i ) { // The nhw position. - int nhw_regs = cta_nhw_regs + loop_i*pixels_per_iteration; + int32_t nhw_regs = cta_nhw_regs + loop_i*pixels_per_iteration; cta_count += max(min(nhw_regs + PIXELS_PER_CTA_IN_REGISTERS, params.nhw) - max(nhw_regs, 0), 0); @@ -171,22 +170,22 @@ // Read the elements from memory. float is_valid[PIXELS_PER_THREAD_IN_REGISTERS]; #pragma unroll - for( int i = 0; i < PIXELS_PER_THREAD_IN_REGISTERS; ++i ) { - const int idx = nhw_regs + thread_in_cta_nhw + i*PIXELS_PER_LDG; + for( int32_t i = 0; i < PIXELS_PER_THREAD_IN_REGISTERS; ++i ) { + const int32_t idx = nhw_regs + thread_in_cta_nhw + i*PIXELS_PER_LDG; zero(x_storage[i]); is_valid[i] = 0.f; if( idx < params.nhw && is_valid_c ) { - ldg_stream(x_storage[i], &gmem_src[idx*stride_c_input]); + ldgStream(x_storage[i], &gmem_src[idx*stride_c_input]); is_valid[i] = 1.f; } } // Do the math. #pragma unroll - for( int i = 0; i < PIXELS_PER_THREAD_IN_REGISTERS; ++i ) { + for( int32_t i = 0; i < PIXELS_PER_THREAD_IN_REGISTERS; ++i ) { // Convert to float. float x_math[ELEMENTS_PER_LDG]; - to_float(x_math, x_storage[i], int8_in_scale); + toFloat(x_math, x_storage[i], int8_in_scale); // Update the count. count += is_valid[i]; @@ -195,7 +194,7 @@ // Update the mean and m2 using deltas. #pragma unroll - for( int j = 0; j < ELEMENTS_PER_LDG; ++j ) { + for( int32_t j = 0; j < ELEMENTS_PER_LDG; ++j ) { float delta0 = x_math[j] - mean[j]; mean[j] += delta0 * inv_count; float delta1 = x_math[j] - mean[j]; @@ -205,25 +204,25 @@ } else { // Read the elements from memory. #pragma unroll - for( int i = 0; i < PIXELS_PER_THREAD_IN_REGISTERS; ++i ) { - const int idx = nhw_regs + thread_in_cta_nhw + i*PIXELS_PER_LDG; + for( int32_t i = 0; i < PIXELS_PER_THREAD_IN_REGISTERS; ++i ) { + const int32_t idx = nhw_regs + thread_in_cta_nhw + i*PIXELS_PER_LDG; zero(x_storage[i]); if( idx < params.nhw && is_valid_c ) { - ldg_stream(x_storage[i], &gmem_src[idx * stride_c_input]); + ldgStream(x_storage[i], &gmem_src[idx * stride_c_input]); count += 1.f; } } // Sum the elements in registers. #pragma unroll - for( int i = 0; i < PIXELS_PER_THREAD_IN_REGISTERS; ++i ) { + for( int32_t i = 0; i < PIXELS_PER_THREAD_IN_REGISTERS; ++i ) { // Convert to float. float x_math[ELEMENTS_PER_LDG]; - to_float(x_math, x_storage[i], int8_in_scale); + toFloat(x_math, x_storage[i], int8_in_scale); // Update the mean and m2 using deltas. #pragma unroll - for( int j = 0; j < ELEMENTS_PER_LDG; ++j ) { + for( int32_t j = 0; j < ELEMENTS_PER_LDG; ++j ) { mean[j] += x_math[j]; } } @@ -231,22 +230,22 @@ // Compute the mean. float inv_count = 1.f / count; #pragma unroll - for( int j = 0; j < ELEMENTS_PER_LDG; ++j ) { + for( int32_t j = 0; j < ELEMENTS_PER_LDG; ++j ) { mean[j] *= inv_count; } // Compute the variance. #pragma unroll - for( int i = 0; i < PIXELS_PER_THREAD_IN_REGISTERS; ++i ) { + for( int32_t i = 0; i < PIXELS_PER_THREAD_IN_REGISTERS; ++i ) { // Convert to float. float x_math[ELEMENTS_PER_LDG]; - to_float(x_math, x_storage[i], int8_in_scale); + toFloat(x_math, x_storage[i], int8_in_scale); // Is it a valid pixel? - float is_valid = i < (int) count ? 1.f : 0.f; + float is_valid = i < (int32_t) count ? 1.f : 0.f; // Update the mean and m2 using deltas. #pragma unroll - for( int j = 0; j < ELEMENTS_PER_LDG; ++j ) { + for( int32_t j = 0; j < ELEMENTS_PER_LDG; ++j ) { m2[j] += (x_math[j] - mean[j]) * (x_math[j] - mean[j]) * is_valid; } } @@ -254,32 +253,32 @@ } // The elements to load and store in SMEM. - int smem_nhw = OUTER_LOOPS*pixels_per_iteration + cta_nhw_smem; + int32_t smem_nhw = OUTER_LOOPS*pixels_per_iteration + cta_nhw_smem; // Load elements from SMEM, update the CTA count. - int pixels_in_smem = min(smem_nhw + PIXELS_PER_CTA_IN_SMEM, params.nhw) - max(smem_nhw, 0); + int32_t pixels_in_smem = min(smem_nhw + PIXELS_PER_CTA_IN_SMEM, params.nhw) - max(smem_nhw, 0); if( pixels_in_smem > 0 ) { cta_count += pixels_in_smem; - for( int i = 0; i < PIXELS_PER_THREAD_IN_SMEM; ++i ) { - const int idx = smem_nhw + thread_in_cta_nhw + i*PIXELS_PER_LDG; + for( int32_t i = 0; i < PIXELS_PER_THREAD_IN_SMEM; ++i ) { + const int32_t idx = smem_nhw + thread_in_cta_nhw + i*PIXELS_PER_LDG; float is_pixel_valid = (idx < params.nhw && is_valid_c) ? 1.f : 0.f; PackedStorageType x_storage_local[PACKED_ELEMENTS_PER_LDG]; - ldg_stream(x_storage_local, &gmem_src[(is_pixel_valid ? idx : 0) * stride_c_input]); + ldgStream(x_storage_local, &gmem_src[(is_pixel_valid ? idx : 0) * stride_c_input]); // The offset to store in SMEM. - const int offset = i*THREADS_PER_CTA*PACKED_ELEMENTS_PER_LDG; + const int32_t offset = i*THREADS_PER_CTA*PACKED_ELEMENTS_PER_LDG; // Store in SMEM. - write_to_smem(&smem_storage[offset], threadIdx.x, x_storage_local); + writeToSmem(&smem_storage[offset], threadIdx.x, x_storage_local); // Update the count. count += is_pixel_valid; // Invert the count. float inv_count = is_pixel_valid ? 1.f / count : 0.f; float x_math[ELEMENTS_PER_LDG]; - to_float(x_math, x_storage_local, int8_in_scale); + toFloat(x_math, x_storage_local, int8_in_scale); // Update the mean and m2 using deltas. #pragma unroll - for( int j = 0; j < ELEMENTS_PER_LDG; ++j ) { + for( int32_t j = 0; j < ELEMENTS_PER_LDG; ++j ) { float delta0 = x_math[j] - mean[j]; mean[j] += delta0 * inv_count; float delta1 = x_math[j] - mean[j]; @@ -291,7 +290,7 @@ // We scale the mean by the number of elements. It brings more stability. float m1[ELEMENTS_PER_LDG]; #pragma unroll - for( int i = 0; i < ELEMENTS_PER_LDG; ++i ) { + for( int32_t i = 0; i < ELEMENTS_PER_LDG; ++i ) { m1[i] = mean[i] * count; } @@ -301,13 +300,13 @@ __syncthreads(); // The values in shared memory correspond to the CTA-wide sums. - read_from_smem(m1, smem, thread_in_cta_c); + readFromSmem(m1, smem, thread_in_cta_c); __syncthreads(); // Adjust the variance. float inv_cta_count = 1.f / (float) cta_count; #pragma unroll - for( int i = 0; i < ELEMENTS_PER_LDG; ++i ) { + for( int32_t i = 0; i < ELEMENTS_PER_LDG; ++i ) { float mean_diff = m1[i]*inv_cta_count - mean[i]; m2[i] = m2[i] + mean_diff * mean_diff * count; } @@ -317,18 +316,18 @@ smem, m2, thread_in_cta_nhw); // The workspace in global memory is distributed across the different CTA. - int gmem_sums_offset = nc_blk_index*gridDim.x*C_ELEMENTS_PER_CTA*2; + int32_t gmem_sums_offset = nc_blk_index*gridDim.x*C_ELEMENTS_PER_CTA*2; // Write the data for the CTA to global memory. GMEM_SUMS_TYPE *gmem_sums = ¶ms.gmem_sums[gmem_sums_offset]; if( threadIdx.x < THREADS_PER_PIXEL ) { - const int idx = blockIdx.x*THREADS_PER_PIXEL + threadIdx.x; - write_to_gmem(&gmem_sums[ 0], idx, m1); - write_to_gmem(&gmem_sums[C_ELEMENTS_PER_CTA*gridDim.x], idx, m2); + const int32_t idx = blockIdx.x*THREADS_PER_PIXEL + threadIdx.x; + writeToGmem(&gmem_sums[ 0], idx, m1); + writeToGmem(&gmem_sums[C_ELEMENTS_PER_CTA*gridDim.x], idx, m2); } // The memory location to store the number of pixels per CTA. - int *gmem_counts = ¶ms.gmem_counts[nc_blk_index*gridDim.x]; + int32_t *gmem_counts = ¶ms.gmem_counts[nc_blk_index*gridDim.x]; if( threadIdx.x == 0 ) { //gmem_counts[0] = cta_count; gmem_counts[blockIdx.x] = cta_count; @@ -337,22 +336,22 @@ // Read the bias and scale. float bias[ELEMENTS_PER_LDG]; float scale[ELEMENTS_PER_LDG]; - read_from_gmem(bias, ¶ms.gmem_bias[cta_c], thread_in_cta_c); - read_from_gmem(scale, ¶ms.gmem_scale[cta_c], thread_in_cta_c); + readFromGmem(bias, ¶ms.gmem_bias[cta_c], thread_in_cta_c); + readFromGmem(scale, ¶ms.gmem_scale[cta_c], thread_in_cta_c); // The counters to count how many CTAs have retired at this point. One per chunk of C. - int *gmem_retired_ctas = ¶ms.gmem_retired_ctas[nc_blk_index]; + int32_t *gmem_retired_ctas = ¶ms.gmem_retired_ctas[nc_blk_index]; // Make sure the threads are done and reconverged. __syncthreads(); // Register the CTA. - int expected_count = gridDim.x; + int32_t expected_count = gridDim.x; if( threadIdx.x == 0 ) { // Issue the membar. __threadfence(); // Notify that the CTA is done. - int val_to_add = 1; + int32_t val_to_add = 1; if (blockIdx.x == 0) { val_to_add = -(expected_count - 1); } @@ -361,7 +360,7 @@ // Are all CTAs done? if (threadIdx.x == 0) { - int retired_ctas = -1; + int32_t retired_ctas = -1; do { __threadfence(); asm volatile("ld.global.cg.b32 %0, [%1];" : "=r"(retired_ctas) : "l"(gmem_retired_ctas)); @@ -372,18 +371,18 @@ // Reset the mean to compute the global mean. #pragma unroll - for( int i = 0; i < ELEMENTS_PER_LDG; ++i ) { + for( int32_t i = 0; i < ELEMENTS_PER_LDG; ++i ) { m1[i] = 0.f; } // Build the global mean. #pragma unroll 1 - for( int idx = threadIdx.x; idx < THREADS_PER_PIXEL*gridDim.x; idx += THREADS_PER_CTA ) { + for( int32_t idx = threadIdx.x; idx < THREADS_PER_PIXEL*gridDim.x; idx += THREADS_PER_CTA ) { float tmp[ELEMENTS_PER_LDG]; - read_from_gmem(tmp, gmem_sums, idx); + readFromGmem(tmp, gmem_sums, idx); #pragma unroll - for( int i = 0; i < ELEMENTS_PER_LDG; ++i ) { + for( int32_t i = 0; i < ELEMENTS_PER_LDG; ++i ) { m1[i] += tmp[i]; } } @@ -394,30 +393,30 @@ __syncthreads(); // The values in shared memory correspond to the CTA-wide sums. - read_from_smem(m1, smem, thread_in_cta_c); + readFromSmem(m1, smem, thread_in_cta_c); __syncthreads(); // Normalize the mean. float inv_count = 1.f / (float) params.nhw; #pragma unroll - for( int i = 0; i < ELEMENTS_PER_LDG; ++i ) { + for( int32_t i = 0; i < ELEMENTS_PER_LDG; ++i ) { m1[i] = m1[i] * inv_count; } // Reset the variance. #pragma unroll - for( int i = 0; i < ELEMENTS_PER_LDG; ++i ) { + for( int32_t i = 0; i < ELEMENTS_PER_LDG; ++i ) { m2[i] = 0.f; } // Build the global variance. #pragma unroll 1 - for( int idx = threadIdx.x; idx < THREADS_PER_PIXEL*gridDim.x; idx += THREADS_PER_CTA ) { + for( int32_t idx = threadIdx.x; idx < THREADS_PER_PIXEL*gridDim.x; idx += THREADS_PER_CTA ) { // Read the means computed by different CTAs (again). Reuse tmp if we have 1 iteration. float tmp_mean[ELEMENTS_PER_LDG], tmp_var[ELEMENTS_PER_LDG]; - read_from_gmem(tmp_mean, &gmem_sums[ 0], idx); - read_from_gmem(tmp_var, &gmem_sums[C_ELEMENTS_PER_CTA*gridDim.x], idx); + readFromGmem(tmp_mean, &gmem_sums[ 0], idx); + readFromGmem(tmp_var, &gmem_sums[C_ELEMENTS_PER_CTA*gridDim.x], idx); // Read the number of pixels visited by a given CTA. cta_count = __ldg(&gmem_counts[idx / THREADS_PER_PIXEL]); @@ -425,13 +424,13 @@ // Compute the diff to update the variance. float mean_diff[ELEMENTS_PER_LDG], inv_cta_count = 1.f / (float) cta_count; #pragma unroll - for( int i = 0; i < ELEMENTS_PER_LDG; ++i ) { + for( int32_t i = 0; i < ELEMENTS_PER_LDG; ++i ) { mean_diff[i] = m1[i] - tmp_mean[i]*inv_cta_count; } // Update the variance. #pragma unroll - for( int i = 0; i < ELEMENTS_PER_LDG; ++i ) { + for( int32_t i = 0; i < ELEMENTS_PER_LDG; ++i ) { m2[i] += tmp_var[i] + mean_diff[i]*mean_diff[i]*(float) cta_count; } } @@ -441,12 +440,12 @@ smem, m2, thread_in_cta_nhw); __syncthreads(); - read_from_smem(m2, smem, thread_in_cta_c); + readFromSmem(m2, smem, thread_in_cta_c); __syncthreads(); // Finalize the stddev. #pragma unroll - for( int i = 0; i < ELEMENTS_PER_LDG; ++i ) { + for( int32_t i = 0; i < ELEMENTS_PER_LDG; ++i ) { m2[i] *= inv_count; } @@ -454,16 +453,16 @@ float svarinv[ELEMENTS_PER_LDG]; bool is_valid_for_saving = is_valid_c && blockIdx.x == 0 && thread_in_cta_nhw == 0; #pragma unroll - for( int i = 0; i < ELEMENTS_PER_LDG; ++i ) { + for( int32_t i = 0; i < ELEMENTS_PER_LDG; ++i ) { svarinv[i] = rsqrtf(m2[i] + params.var_eps); } - - #if !DISABLE_MEAN_VAR_OUTPUT - int global_stats_offset = n_blk_index * params.c; + + #if ACCUM_MEAN_VAR_IN_FLOAT + int32_t global_stats_offset = n_blk_index * params.c; if( is_valid_for_saving ) { - write_to_gmem(params.gmem_saved_mean + global_stats_offset, \ + writeToGmem(params.gmem_saved_mean + global_stats_offset, \ thread_c/ELEMENTS_PER_LDG, m1); - write_to_gmem(params.gmem_saved_var + global_stats_offset, \ + writeToGmem(params.gmem_saved_var + global_stats_offset, \ thread_c/ELEMENTS_PER_LDG, svarinv); } @@ -473,27 +472,27 @@ zero(rmean); zero(rvar); if( params.exp_avg_factor != 1.f && is_valid_for_saving ) { - read_from_gmem(rmean, params.gmem_running_mean + global_stats_offset, \ + readFromGmem(rmean, params.gmem_running_mean + global_stats_offset, \ thread_c/ELEMENTS_PER_LDG); - read_from_gmem(rvar, params.gmem_running_var + global_stats_offset, \ + readFromGmem(rvar, params.gmem_running_var + global_stats_offset, \ thread_c/ELEMENTS_PER_LDG); } #pragma unroll - for( int i = 0; i < ELEMENTS_PER_LDG; ++i ) { + for( int32_t i = 0; i < ELEMENTS_PER_LDG; ++i ) { rmean[i] = (1.f - params.exp_avg_factor) * rmean[i] + \ params.exp_avg_factor * m1[i]; rvar[i] = (1.f - params.exp_avg_factor) * rvar[i] + \ params.exp_avg_factor * m2[i]; } if( is_valid_for_saving ) { - write_to_gmem(params.gmem_running_mean + global_stats_offset, thread_c/ELEMENTS_PER_LDG, rmean); - write_to_gmem(params.gmem_running_var + global_stats_offset, thread_c/ELEMENTS_PER_LDG, rvar); + writeToGmem(params.gmem_running_mean + global_stats_offset, thread_c/ELEMENTS_PER_LDG, rmean); + writeToGmem(params.gmem_running_var + global_stats_offset, thread_c/ELEMENTS_PER_LDG, rvar); } #endif // Update the scale with the stddev and eps. #pragma unroll - for( int i = 0; i < ELEMENTS_PER_LDG; ++i ) { + for( int32_t i = 0; i < ELEMENTS_PER_LDG; ++i ) { scale[i] *= svarinv[i]; } @@ -502,28 +501,28 @@ // Store the elements in registers. #pragma unroll 1 - for( int loop_i = OUTER_LOOPS-1; loop_i >= 0; --loop_i ) { + for( int32_t loop_i = OUTER_LOOPS-1; loop_i >= 0; --loop_i ) { // The value for nhw. - int out_nhw = cta_nhw_regs + loop_i*pixels_per_iteration; + int32_t out_nhw = cta_nhw_regs + loop_i*pixels_per_iteration; // Normalize the elements and write to memory. #pragma unroll - for( int i = 0; i < PIXELS_PER_THREAD_IN_REGISTERS; ++i ) { + for( int32_t i = 0; i < PIXELS_PER_THREAD_IN_REGISTERS; ++i ) { // Convert to float. float x_math[ELEMENTS_PER_LDG]; - to_float(x_math, x_storage[i], int8_in_scale); + toFloat(x_math, x_storage[i], int8_in_scale); // Normalize and apply activation function normalize(x_math, bias, scale, m1); if( params.use_relu ) { - relu_activation(x_math, params.relu_alpha); + reluActivation(x_math, params.relu_alpha); } // Write back. - const int idx = out_nhw + thread_in_cta_nhw + i*PIXELS_PER_LDG; + const int32_t idx = out_nhw + thread_in_cta_nhw + i*PIXELS_PER_LDG; if( (unsigned) idx < params.nhw && is_valid_c ) { - stg_stream(&gmem_dst[idx*stride_c_output], x_math, int8_out_scale); + stgStream(&gmem_dst[idx*stride_c_output], x_math, int8_out_scale); } } @@ -532,34 +531,34 @@ // Read the next elements from memory. #pragma unroll - for( int i = 0; i < PIXELS_PER_THREAD_IN_REGISTERS; ++i ) { - const int idx = out_nhw + thread_in_cta_nhw + i*PIXELS_PER_LDG; + for( int32_t i = 0; i < PIXELS_PER_THREAD_IN_REGISTERS; ++i ) { + const int32_t idx = out_nhw + thread_in_cta_nhw + i*PIXELS_PER_LDG; if( (unsigned) idx < params.nhw && is_valid_c ) { - ldg_stream(x_storage[i], &gmem_src[idx*stride_c_output]); + ldgStream(x_storage[i], &gmem_src[idx*stride_c_output]); } } } // Normalize the elements from SMEM and write them out. if( pixels_in_smem > 0 ) { - for( int i = 0; i < PIXELS_PER_THREAD_IN_SMEM; ++i ) { + for( int32_t i = 0; i < PIXELS_PER_THREAD_IN_SMEM; ++i ) { // Read from SMEM. - const int offset = i*THREADS_PER_CTA*PACKED_ELEMENTS_PER_LDG; + const int32_t offset = i*THREADS_PER_CTA*PACKED_ELEMENTS_PER_LDG; float x_math[ELEMENTS_PER_LDG]; PackedStorageType x_storage_local[PACKED_ELEMENTS_PER_LDG]; - read_from_smem(x_storage_local, &smem_storage[offset], threadIdx.x); - to_float(x_math, x_storage_local, int8_in_scale); + readFromSmem(x_storage_local, &smem_storage[offset], threadIdx.x); + toFloat(x_math, x_storage_local, int8_in_scale); // Normalize and apply activation function normalize(x_math, bias, scale, m1); if( params.use_relu ) { - relu_activation(x_math, params.relu_alpha); + reluActivation(x_math, params.relu_alpha); } // Write back. - const int idx = smem_nhw + thread_in_cta_nhw + i*PIXELS_PER_LDG; + const int32_t idx = smem_nhw + thread_in_cta_nhw + i*PIXELS_PER_LDG; if( (unsigned) idx < params.nhw && is_valid_c ) { - stg_stream(&gmem_dst[idx*stride_c_output], x_math, int8_out_scale); + stgStream(&gmem_dst[idx*stride_c_output], x_math, int8_out_scale); } } } @@ -570,11 +569,11 @@ template - dim3 estimate_in_grid_dim(const InstanceNormFwdParams& params) + dim3 estimateInGridDim(const InstanceNormFwdParams& params) { dim3 grid_dim; - grid_dim.x = div_up(params.nhw, Kernel_params::MIN_PIXELS_PER_CTA); // PIXELS_PER_CTA - grid_dim.y = div_up(params.c * params.n, Kernel_params::C_ELEMENTS_PER_CTA); + grid_dim.x = divUp(params.nhw, Kernel_params::MIN_PIXELS_PER_CTA); // PIXELS_PER_CTA + grid_dim.y = divUp(params.c * params.n, Kernel_params::C_ELEMENTS_PER_CTA); grid_dim.z = 1; //params.n; return grid_dim; @@ -582,18 +581,18 @@ template - void instance_norm_buffer_sizes(const InstanceNormFwdParams& params, + void instanceNormBufferSizes(const InstanceNormFwdParams& params, size_t &size_sums, size_t &size_counts, size_t &size_retired_ctas) { - dim3 grid_dim = estimate_in_grid_dim(params); + dim3 grid_dim = estimateInGridDim(params); size_sums = grid_dim.z*grid_dim.y*grid_dim.x*Kernel_params::THREADS_PER_PIXEL*Kernel_params::ELEMENTS_PER_LDG*2*sizeof(GMEM_SUMS_TYPE); - size_counts = grid_dim.z*grid_dim.y*grid_dim.x*sizeof(int); - size_retired_ctas = grid_dim.z*grid_dim.y*sizeof(int); + size_counts = grid_dim.z*grid_dim.y*grid_dim.x*sizeof(int32_t); + size_retired_ctas = grid_dim.z*grid_dim.y*sizeof(int32_t); - size_sums = div_up(size_sums, 256) * 256; - size_counts = div_up(size_counts, 256) * 256; - size_retired_ctas = div_up(size_retired_ctas, 256) * 256; + size_sums = divUp(size_sums, 256) * 256; + size_counts = divUp(size_counts, 256) * 256; + size_retired_ctas = divUp(size_retired_ctas, 256) * 256; } @@ -601,25 +600,25 @@ template - int instance_norm_fwd_launch(const InstanceNormFwdContext& context, InstanceNormFwdParams& params, cudaStream_t stream) + int32_t instance_norm_fwd_launch(const InstanceNormFwdContext& context, InstanceNormFwdParams& params, cudaStream_t stream) { size_t smem_size = Kernel_params::PIXELS_PER_THREAD_IN_SMEM * Kernel_params::THREADS_PER_CTA * Kernel_params::ELEMENTS_PER_LDG * sizeof(typename Kernel_params::StorageType); - dim3 grid_dim = estimate_in_grid_dim(params); + dim3 grid_dim = estimateInGridDim(params); - params.c_blks = div_up(params.c, Kernel_params::C_ELEMENTS_PER_CTA); + params.c_blks = divUp(params.c, Kernel_params::C_ELEMENTS_PER_CTA); - size_t size_retired_ctas = grid_dim.z*grid_dim.y*sizeof(int); + size_t size_retired_ctas = grid_dim.z*grid_dim.y*sizeof(int32_t); #define KERNEL_RUN(OUTER_LOOPS, DESIRED_OCCUPANCY) \ { \ CHECK_CUDA(cudaMemsetAsync(params.gmem_retired_ctas, 0, size_retired_ctas, stream)); \ if( smem_size > 0 ) \ CHECK_CUDA(cudaFuncSetAttribute( \ - instance_norm_fwd< \ + instanceNormFwd< \ typename Kernel_params::StorageType, \ typename Kernel_params::Input_Data_Type, \ typename Kernel_params::Output_Data_Type, \ @@ -633,7 +632,7 @@ DESIRED_OCCUPANCY>, \ cudaFuncAttributeMaxDynamicSharedMemorySize, \ smem_size)); \ - instance_norm_fwd< \ + instanceNormFwd< \ typename Kernel_params::StorageType, \ typename Kernel_params::Input_Data_Type, \ typename Kernel_params::Output_Data_Type, \ @@ -647,22 +646,22 @@ DESIRED_OCCUPANCY><<>>(params); } size_t total_smem_bytes = smem_size + Kernel_params::ELEMENTS_PER_LDG * Kernel_params::THREADS_PER_CTA * sizeof(float); - int smem_driven_fwd_occupancy = min(int(context.sm_shared_size) / (int)total_smem_bytes, (int)2); - int max_grid = context.sm_count * smem_driven_fwd_occupancy; + int32_t smem_driven_fwd_occupancy = min(int32_t(context.sm_shared_size) / (int32_t)total_smem_bytes, (int32_t)2); + int32_t max_grid = context.sm_count * smem_driven_fwd_occupancy; if ((context.sm_version >= 700 ) && (context.sm_version < 800)) { max_grid = max_grid - 4; } - if (max_grid / int(grid_dim.x) > 1) { - grid_dim.y = max_grid / int(grid_dim.x); - grid_dim.y = int(grid_dim.y) > params.c_blks * params.n ? params.c_blks * params.n : int(grid_dim.y); + if (max_grid / int32_t(grid_dim.x) > 1) { + grid_dim.y = max_grid / int32_t(grid_dim.x); + grid_dim.y = int32_t(grid_dim.y) > params.c_blks * params.n ? params.c_blks * params.n : int32_t(grid_dim.y); } else { grid_dim.y = 1; } - int loop = 1; - if( grid_dim.x <= max_grid ) { + int32_t loop = 1; + if( int32_t(grid_dim.x) <= max_grid ) { if (smem_driven_fwd_occupancy >= 2) { KERNEL_RUN(1, 2); } else { @@ -670,8 +669,8 @@ } } else { grid_dim.x = max_grid; - int nhw_in_regs = params.nhw - Kernel_params::PIXELS_PER_THREAD_IN_SMEM*Kernel_params::PIXELS_PER_LDG*grid_dim.x; - int pixels_per_iteration = Kernel_params::PIXELS_PER_THREAD_IN_REGISTERS*Kernel_params::PIXELS_PER_LDG*grid_dim.x; + int32_t nhw_in_regs = params.nhw - Kernel_params::PIXELS_PER_THREAD_IN_SMEM*Kernel_params::PIXELS_PER_LDG*grid_dim.x; + int32_t pixels_per_iteration = Kernel_params::PIXELS_PER_THREAD_IN_REGISTERS*Kernel_params::PIXELS_PER_LDG*grid_dim.x; nhw_in_regs = (nhw_in_regs <= 0)? pixels_per_iteration : nhw_in_regs; if (nhw_in_regs < 0) { @@ -680,7 +679,7 @@ assert(pixels_per_iteration >= params.nhw); } - loop = div_up(nhw_in_regs, pixels_per_iteration); + loop = divUp(nhw_in_regs, pixels_per_iteration); params.outer_loops = loop; assert(loop >= 1); @@ -701,31 +700,31 @@ return loop; } - static int c_cond_g = 32; + static int32_t c_cond_g = 32; - void instance_norm_buffer_sizes_dispatch(const InstanceNormFwdContext& context, const InstanceNormFwdParams& params, + void instanceNormBufferSizesDispatch(const InstanceNormFwdContext& context, const InstanceNormFwdParams& params, size_t &size_sums, size_t &size_counts, size_t &size_retired_ctas, - int input_data_type, int output_data_type) + int32_t input_data_type, int32_t output_data_type) { if (input_data_type == 2 && output_data_type == 2) { switch (context.sm_version) { - case 700: return instance_norm_buffer_sizes(params, size_sums, size_counts, size_retired_ctas); break; - case 720: return instance_norm_buffer_sizes(params, size_sums, size_counts, size_retired_ctas); break; - case 750: return instance_norm_buffer_sizes(params, size_sums, size_counts, size_retired_ctas); break; - case 800: return instance_norm_buffer_sizes(params, size_sums, size_counts, size_retired_ctas); break; - case 860: return instance_norm_buffer_sizes(params, size_sums, size_counts, size_retired_ctas); break; - default: return instance_norm_buffer_sizes(params, size_sums, size_counts, size_retired_ctas); break; + case 700: return instanceNormBufferSizes(params, size_sums, size_counts, size_retired_ctas); break; + case 720: return instanceNormBufferSizes(params, size_sums, size_counts, size_retired_ctas); break; + case 750: return instanceNormBufferSizes(params, size_sums, size_counts, size_retired_ctas); break; + case 800: return instanceNormBufferSizes(params, size_sums, size_counts, size_retired_ctas); break; + case 860: return instanceNormBufferSizes(params, size_sums, size_counts, size_retired_ctas); break; + default: return instanceNormBufferSizes(params, size_sums, size_counts, size_retired_ctas); break; } - return instance_norm_buffer_sizes(params, size_sums, size_counts, size_retired_ctas); + return instanceNormBufferSizes(params, size_sums, size_counts, size_retired_ctas); } else if (input_data_type == 1 && output_data_type == 2) { - return instance_norm_buffer_sizes(params, size_sums, size_counts, size_retired_ctas); + return instanceNormBufferSizes(params, size_sums, size_counts, size_retired_ctas); } else if (input_data_type == 1 && output_data_type == 1) { if (params.c <= c_cond_g) { - return instance_norm_buffer_sizes(params, size_sums, size_counts, size_retired_ctas); + return instanceNormBufferSizes(params, size_sums, size_counts, size_retired_ctas); } else { - return instance_norm_buffer_sizes(params, size_sums, size_counts, size_retired_ctas); + return instanceNormBufferSizes(params, size_sums, size_counts, size_retired_ctas); } } else { fprintf(stderr, "Unsupported format combination by the instance norm kernel\n"); @@ -734,8 +733,8 @@ } - int instance_norm_fwd_dispatch(const InstanceNormFwdContext& context, InstanceNormFwdParams& params, cudaStream_t stream, - int input_data_type, int output_data_type) + int32_t instanceNormFwdDispatch(const InstanceNormFwdContext& context, InstanceNormFwdParams& params, cudaStream_t stream, + int32_t input_data_type, int32_t output_data_type) { assert(context.sm_version >= 600); if (input_data_type == 2 && output_data_type == 2) { diff --git a/plugin/instanceNormalizationPlugin/instanceNormalizationPlugin.cu b/plugin/instanceNormalizationPlugin/instanceNormalizationPlugin.cu index 9840cf62..140adf60 100644 --- a/plugin/instanceNormalizationPlugin/instanceNormalizationPlugin.cu +++ b/plugin/instanceNormalizationPlugin/instanceNormalizationPlugin.cu @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +#include "checkMacrosPlugin.h" #include "instanceNormalizationPlugin.h" #include #include @@ -22,17 +22,11 @@ using namespace nvinfer1; using nvinfer1::plugin::InstanceNormalizationPlugin; using nvinfer1::plugin::InstanceNormalizationPluginCreator; -inline bool is_CHW(nvinfer1::Dims const& dims) +template +__global__ __launch_bounds__(THREADS_PER_CTA) void in3dReluActivation( + T* __restrict dst, T* __restrict src, float alpha, int32_t count) { - return (dims.nbDims == 3 && dims.type[0] == nvinfer1::DimensionType::kCHANNEL - && dims.type[1] == nvinfer1::DimensionType::kSPATIAL && dims.type[2] == nvinfer1::DimensionType::kSPATIAL); -} - -template -__global__ __launch_bounds__(THREADS_PER_CTA) void in3d_relu_activation( - T* __restrict dst, T* __restrict src, float alpha, int count) -{ - int idx = blockIdx.x * THREADS_PER_CTA + threadIdx.x; + int32_t idx = blockIdx.x * THREADS_PER_CTA + threadIdx.x; if (idx >= count) return; @@ -40,27 +34,7 @@ __global__ __launch_bounds__(THREADS_PER_CTA) void in3d_relu_activation( dst[idx] = (val < 0.f) ? val * alpha : val; } -// This is derived from: https://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/ -inline float half_to_float_fast(unsigned short value) -{ - union F32 { - unsigned int u; - float f; - }; - static const F32 magic = {(254 - 15) << 23}; - static const F32 was_infnan = {(127 + 16) << 23}; - F32 result; - result.u = (value & 0x7fff) << 13; // exponent/mantissa bits - result.f *= magic.f; // exponent adjust - if (result.f >= was_infnan.f) - { // make sure Inf/NaN survive - result.u |= 255 << 23; - } - result.u |= (value & 0x8000) << 16; // sign bit - return result.f; -} - -cudnnStatus_t convert_trt2cudnn_dtype(nvinfer1::DataType trt_dtype, cudnnDataType_t* cudnn_dtype) +cudnnStatus_t convertTrt2cudnnDtype(nvinfer1::DataType trt_dtype, cudnnDataType_t* cudnn_dtype) { switch (trt_dtype) { @@ -81,7 +55,7 @@ PluginFieldCollection InstanceNormalizationPluginCreator::mFC{}; std::vector InstanceNormalizationPluginCreator::mPluginAttributes; InstanceNormalizationPlugin::InstanceNormalizationPlugin( - float epsilon, const std::vector& scale, const std::vector& bias, int relu, float alpha) + float epsilon, const std::vector& scale, const std::vector& bias, int32_t relu, float alpha) : mEpsilon(epsilon) , mNchan(scale.size()) , mHostScale(scale) @@ -98,7 +72,7 @@ InstanceNormalizationPlugin::InstanceNormalizationPlugin( } InstanceNormalizationPlugin::InstanceNormalizationPlugin( - float epsilon, nvinfer1::Weights const& scale, nvinfer1::Weights const& bias, int relu, float alpha) + float epsilon, nvinfer1::Weights const& scale, nvinfer1::Weights const& bias, int32_t relu, float alpha) : mEpsilon(epsilon) , mNchan(scale.count) , mRelu(relu) @@ -117,7 +91,7 @@ InstanceNormalizationPlugin::InstanceNormalizationPlugin( else if (scale.type == nvinfer1::DataType::kHALF) { mHostScale.reserve(mNchan); - for (int c = 0; c < mNchan; ++c) + for (int32_t c = 0; c < mNchan; ++c) { unsigned short value = ((unsigned short*) scale.values)[c]; mHostScale.push_back(__internal_half2float(value)); @@ -134,7 +108,7 @@ InstanceNormalizationPlugin::InstanceNormalizationPlugin( else if (bias.type == nvinfer1::DataType::kHALF) { mHostBias.reserve(mNchan); - for (int c = 0; c < mNchan; ++c) + for (int32_t c = 0; c < mNchan; ++c) { unsigned short value = ((unsigned short*) bias.values)[c]; mHostBias.push_back(__internal_half2float(value)); @@ -164,19 +138,19 @@ InstanceNormalizationPlugin::~InstanceNormalizationPlugin() } // InstanceNormalizationPlugin returns one output. -int InstanceNormalizationPlugin::getNbOutputs() const +int32_t InstanceNormalizationPlugin::getNbOutputs() const noexcept { return 1; } -DimsExprs InstanceNormalizationPlugin::getOutputDimensions( - int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) +DimsExprs InstanceNormalizationPlugin::getOutputDimensions(int32_t outputIndex, const nvinfer1::DimsExprs* inputs, + int32_t nbInputs, nvinfer1::IExprBuilder& exprBuilder) noexcept { nvinfer1::DimsExprs output(inputs[0]); return output; } -int InstanceNormalizationPlugin::initialize() +int32_t InstanceNormalizationPlugin::initialize() noexcept { if (!mInitialized) { @@ -188,7 +162,7 @@ int InstanceNormalizationPlugin::initialize() // NDHWC path // Device info. - int device; + int32_t device; CHECK_CUDA(cudaGetDevice(&device)); cudaDeviceProp props; CHECK_CUDA(cudaGetDeviceProperties(&props, device)); @@ -209,7 +183,7 @@ int InstanceNormalizationPlugin::initialize() return 0; } -void InstanceNormalizationPlugin::terminate() +void InstanceNormalizationPlugin::terminate() noexcept { if (mInitialized) { @@ -219,14 +193,14 @@ void InstanceNormalizationPlugin::terminate() cudnnDestroy(mCudnnHandle); - cudaFree(mDeviceBias); - cudaFree(mDeviceScale); + CUASSERT(cudaFree(mDeviceBias)); + CUASSERT(cudaFree(mDeviceScale)); } mInitialized = false; } -size_t InstanceNormalizationPlugin::getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int nbInputs, - const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const +size_t InstanceNormalizationPlugin::getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int32_t nbInputs, + const nvinfer1::PluginTensorDesc* outputs, int32_t nbOutputs) const noexcept { nvinfer1::Dims input_dims = inputs[0].dims; @@ -239,8 +213,8 @@ size_t InstanceNormalizationPlugin::getWorkspaceSize(const nvinfer1::PluginTenso { nvinfer1::Dims input_dims = inputs[0].dims; - int n = input_dims.d[0]; - int c = input_dims.d[1]; + int32_t n = input_dims.d[0]; + int32_t c = input_dims.d[1]; size_t nchan_bytes = c * sizeof(float); size_t scale_size = n * nchan_bytes; @@ -252,15 +226,15 @@ size_t InstanceNormalizationPlugin::getWorkspaceSize(const nvinfer1::PluginTenso } else if (inputs[0].format == nvinfer1::PluginFormat::kDHWC8 || inputs[0].format == nvinfer1::PluginFormat::kCDHW32) { - int input_data_type = (inputs[0].type == nvinfer1::DataType::kHALF) ? 1 : 2; - int output_data_type = (outputs[0].type == nvinfer1::DataType::kHALF) ? 1 : 2; + int32_t input_data_type = (inputs[0].type == nvinfer1::DataType::kHALF) ? 1 : 2; + int32_t output_data_type = (outputs[0].type == nvinfer1::DataType::kHALF) ? 1 : 2; nvinfer1::Dims input_dims = inputs[0].dims; - int n = input_dims.d[0]; - int c = input_dims.d[1]; - int d = input_dims.d[2]; - int h = input_dims.d[3]; - int w = input_dims.d[4]; + int32_t n = input_dims.d[0]; + int32_t c = input_dims.d[1]; + int32_t d = input_dims.d[2]; + int32_t h = input_dims.d[3]; + int32_t w = input_dims.d[4]; InstanceNormFwdParams params; // only these parameters are required for workspace computation @@ -269,7 +243,7 @@ size_t InstanceNormalizationPlugin::getWorkspaceSize(const nvinfer1::PluginTenso params.n = n; // Reserve memory for the workspaces. size_t size_sums, size_counts, size_retired_ctas; - instance_norm_buffer_sizes_dispatch( + instanceNormBufferSizesDispatch( mContext, params, size_sums, size_counts, size_retired_ctas, input_data_type, output_data_type); size_t size_nc = n * c * sizeof(float); size_nc = ((size_nc + 256 - 1) / 256) * 256; @@ -279,54 +253,55 @@ size_t InstanceNormalizationPlugin::getWorkspaceSize(const nvinfer1::PluginTenso { ASSERT(0); } + return 0; } -int InstanceNormalizationPlugin::enqueue(const nvinfer1::PluginTensorDesc* inputDesc, +int32_t InstanceNormalizationPlugin::enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, const void* const* inputs, void* const* outputs, void* workspace, - cudaStream_t stream) + cudaStream_t stream) noexcept { nvinfer1::Dims input_dims = inputDesc[0].dims; if (input_dims.nbDims <= 4) { nvinfer1::Dims input_dims = inputDesc[0].dims; - int n = input_dims.d[0]; - int c = input_dims.d[1]; - int h = input_dims.d[2]; - int w = input_dims.d[3] > 0 ? input_dims.d[3] : 1; + int32_t n = input_dims.d[0]; + int32_t c = input_dims.d[1]; + int32_t h = input_dims.d[2]; + int32_t w = input_dims.nbDims > 3 ? input_dims.d[3] : 1; size_t nchan_bytes = c * sizeof(float); // Note: We repeat the data for each batch entry so that we can do the full // computation in a single CUDNN call in enqueue(). if (mDeviceBytes < n * nchan_bytes) { - cudaFree(mDeviceBias); - cudaFree(mDeviceScale); + CUASSERT(cudaFree(mDeviceBias)); + CUASSERT(cudaFree(mDeviceScale)); mDeviceBytes = n * nchan_bytes; - CHECK_CUDA(cudaMalloc((void**) &mDeviceScale, mDeviceBytes)); - CHECK_CUDA(cudaMalloc((void**) &mDeviceBias, mDeviceBytes)); + CUASSERT(cudaMalloc((void**) &mDeviceScale, mDeviceBytes)); + CUASSERT(cudaMalloc((void**) &mDeviceBias, mDeviceBytes)); } - for (int i = 0; i < n; ++i) + for (int32_t i = 0; i < n; ++i) { - CHECK_CUDA(cudaMemcpy(mDeviceScale + i * c, mHostScale.data(), nchan_bytes, cudaMemcpyHostToDevice)); - CHECK_CUDA(cudaMemcpy(mDeviceBias + i * c, mHostBias.data(), nchan_bytes, cudaMemcpyHostToDevice)); + CUASSERT(cudaMemcpy(mDeviceScale + i * c, mHostScale.data(), nchan_bytes, cudaMemcpyHostToDevice)); + CUASSERT(cudaMemcpy(mDeviceBias + i * c, mHostBias.data(), nchan_bytes, cudaMemcpyHostToDevice)); } - CHECK_CUDNN(cudnnSetTensor4dDescriptor(mBDescriptor, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, 1, n * c, 1, 1)); + CUDNNASSERT(cudnnSetTensor4dDescriptor(mBDescriptor, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, 1, n * c, 1, 1)); cudnnDataType_t cudnn_dtype{}; - CHECK_CUDNN(convert_trt2cudnn_dtype(inputDesc[0].type, &cudnn_dtype)); - CHECK_CUDNN(cudnnSetTensor4dDescriptor(mXDescriptor, CUDNN_TENSOR_NCHW, cudnn_dtype, 1, n * c, h, w)); - CHECK_CUDNN(cudnnSetTensor4dDescriptor(mYDescriptor, CUDNN_TENSOR_NCHW, cudnn_dtype, 1, n * c, h, w)); + CUDNNASSERT(convertTrt2cudnnDtype(inputDesc[0].type, &cudnn_dtype)); + CUDNNASSERT(cudnnSetTensor4dDescriptor(mXDescriptor, CUDNN_TENSOR_NCHW, cudnn_dtype, 1, n * c, h, w)); + CUDNNASSERT(cudnnSetTensor4dDescriptor(mYDescriptor, CUDNN_TENSOR_NCHW, cudnn_dtype, 1, n * c, h, w)); float alpha = 1; float beta = 0; void const* x_ptr = inputs[0]; void* y_ptr = outputs[0]; - CHECK_CUDNN(cudnnSetStream(mCudnnHandle, stream)); + CUDNNASSERT(cudnnSetStream(mCudnnHandle, stream)); // Note: Use of CUDNN_BATCHNORM_SPATIAL_PERSISTENT can cause numerical // overflows (NaNs) for fp32 data in some circumstances. The lower- // performance CUDNN_BATCHNORM_SPATIAL should be used if this is not // acceptable. - CHECK_CUDNN(cudnnBatchNormalizationForwardTraining(mCudnnHandle, CUDNN_BATCHNORM_SPATIAL_PERSISTENT, &alpha, + CUDNNASSERT(cudnnBatchNormalizationForwardTraining(mCudnnHandle, CUDNN_BATCHNORM_SPATIAL_PERSISTENT, &alpha, &beta, mXDescriptor, x_ptr, mYDescriptor, y_ptr, mBDescriptor, mDeviceScale, mDeviceBias, 1., nullptr, nullptr, mEpsilon, nullptr, nullptr)); } @@ -336,11 +311,11 @@ int InstanceNormalizationPlugin::enqueue(const nvinfer1::PluginTensorDesc* input { CHECK_CUDNN(cudnnSetStream(mCudnnHandle, stream)); nvinfer1::Dims input_dims = inputDesc[0].dims; - int n = input_dims.d[0]; - int c = input_dims.d[1]; - int d = input_dims.d[2]; - int h = input_dims.d[3]; - int w = input_dims.d[4]; + int32_t n = input_dims.d[0]; + int32_t c = input_dims.d[1]; + int32_t d = input_dims.d[2]; + int32_t h = input_dims.d[3]; + int32_t w = input_dims.d[4]; size_t nchan_bytes = c * sizeof(float); // Note: We repeat the data for each batch entry so that we can do the full @@ -348,23 +323,23 @@ int InstanceNormalizationPlugin::enqueue(const nvinfer1::PluginTensorDesc* input float* _d_array = (float*) workspace; float* d_scale = &_d_array[0]; float* d_bias = &_d_array[n * c]; - for (int i = 0; i < n; ++i) + for (int32_t i = 0; i < n; ++i) { CHECK_CUDA( cudaMemcpyAsync(d_scale + i * c, mDeviceScale, nchan_bytes, cudaMemcpyDeviceToDevice, stream)); CHECK_CUDA(cudaMemcpyAsync(d_bias + i * c, mDeviceBias, nchan_bytes, cudaMemcpyDeviceToDevice, stream)); } - int nc_dimA[] = {1, n * c, 1, 1, 1}; - int nc_strideA[] = {nc_dimA[1] * nc_dimA[2] * nc_dimA[3] * nc_dimA[4], nc_dimA[2] * nc_dimA[3] * nc_dimA[4], - nc_dimA[3] * nc_dimA[4], nc_dimA[4], 1}; - int img_dimA[] = {1, n * c, d, h, w}; - int img_strideA[] = {img_dimA[1] * img_dimA[2] * img_dimA[3] * img_dimA[4], + int32_t nc_dimA[] = {1, n * c, 1, 1, 1}; + int32_t nc_strideA[] = {nc_dimA[1] * nc_dimA[2] * nc_dimA[3] * nc_dimA[4], + nc_dimA[2] * nc_dimA[3] * nc_dimA[4], nc_dimA[3] * nc_dimA[4], nc_dimA[4], 1}; + int32_t img_dimA[] = {1, n * c, d, h, w}; + int32_t img_strideA[] = {img_dimA[1] * img_dimA[2] * img_dimA[3] * img_dimA[4], img_dimA[2] * img_dimA[3] * img_dimA[4], img_dimA[3] * img_dimA[4], img_dimA[4], 1}; CHECK_CUDNN(cudnnSetTensorNdDescriptor(mBDescriptor, CUDNN_DATA_FLOAT, 5, nc_dimA, nc_strideA)); cudnnDataType_t cudnn_dtype; - CHECK_CUDNN(convert_trt2cudnn_dtype(inputDesc[0].type, &cudnn_dtype)); + CHECK_CUDNN(convertTrt2cudnnDtype(inputDesc[0].type, &cudnn_dtype)); CHECK_CUDNN(cudnnSetTensorNdDescriptor(mXDescriptor, cudnn_dtype, 5, img_dimA, img_strideA)); CHECK_CUDNN(cudnnSetTensorNdDescriptor(mYDescriptor, cudnn_dtype, 5, img_dimA, img_strideA)); float alpha = 1; @@ -383,16 +358,16 @@ int InstanceNormalizationPlugin::enqueue(const nvinfer1::PluginTensorDesc* input if (mRelu > 0) { - int count = n * c * d * h * w; - const int BLOCK_SZ = 256; + int32_t count = n * c * d * h * w; + const int32_t BLOCK_SZ = 256; if (inputDesc[0].type == nvinfer1::DataType::kFLOAT) { - in3d_relu_activation<<<(count + BLOCK_SZ - 1) / BLOCK_SZ, BLOCK_SZ, 0, stream>>>( + in3dReluActivation<<<(count + BLOCK_SZ - 1) / BLOCK_SZ, BLOCK_SZ, 0, stream>>>( (float*) y_ptr, (float*) y_ptr, mAlpha, count); } else if (inputDesc[0].type == nvinfer1::DataType::kHALF) { - in3d_relu_activation<__half, BLOCK_SZ><<<(count + BLOCK_SZ - 1) / BLOCK_SZ, BLOCK_SZ, 0, stream>>>( + in3dReluActivation<__half, BLOCK_SZ><<<(count + BLOCK_SZ - 1) / BLOCK_SZ, BLOCK_SZ, 0, stream>>>( (__half*) y_ptr, (__half*) y_ptr, mAlpha, count); } else @@ -404,22 +379,22 @@ int InstanceNormalizationPlugin::enqueue(const nvinfer1::PluginTensorDesc* input else if (inputDesc[0].format == nvinfer1::PluginFormat::kDHWC8 || inputDesc[0].format == nvinfer1::PluginFormat::kCDHW32) { - int input_data_type = (inputDesc[0].type == nvinfer1::DataType::kHALF) ? 1 : 2; - int output_data_type = (outputDesc[0].type == nvinfer1::DataType::kHALF) ? 1 : 2; + int32_t input_data_type = (inputDesc[0].type == nvinfer1::DataType::kHALF) ? 1 : 2; + int32_t output_data_type = (outputDesc[0].type == nvinfer1::DataType::kHALF) ? 1 : 2; nvinfer1::Dims input_dims = inputDesc[0].dims; - int n = input_dims.d[0]; - int c = input_dims.d[1]; - int d = input_dims.d[2]; - int h = input_dims.d[3]; - int w = input_dims.d[4]; + int32_t n = input_dims.d[0]; + int32_t c = input_dims.d[1]; + int32_t d = input_dims.d[2]; + int32_t h = input_dims.d[3]; + int32_t w = input_dims.d[4]; mParams.nhw = d * h * w; mParams.c = c; mParams.n = n; size_t size_sums, size_counts, size_retired_ctas; - instance_norm_buffer_sizes_dispatch( + instanceNormBufferSizesDispatch( mContext, mParams, size_sums, size_counts, size_retired_ctas, input_data_type, output_data_type); size_t size_nc = n * c * sizeof(float); @@ -429,9 +404,9 @@ int InstanceNormalizationPlugin::enqueue(const nvinfer1::PluginTensorDesc* input mParams.gmem_sums = reinterpret_cast(d_buf); d_buf += size_sums; - mParams.gmem_counts = reinterpret_cast(d_buf); + mParams.gmem_counts = reinterpret_cast(d_buf); d_buf += size_counts; - mParams.gmem_retired_ctas = reinterpret_cast(d_buf); + mParams.gmem_retired_ctas = reinterpret_cast(d_buf); d_buf += size_retired_ctas; mParams.gmem_running_mean = reinterpret_cast(d_buf); d_buf += size_nc; @@ -455,7 +430,7 @@ int InstanceNormalizationPlugin::enqueue(const nvinfer1::PluginTensorDesc* input mParams.in_scale = mInputScale; mParams.out_scale = 1.f / mOutputScale; - int loop = instance_norm_fwd_dispatch(mContext, mParams, stream, input_data_type, output_data_type); + int32_t loop = instanceNormFwdDispatch(mContext, mParams, stream, input_data_type, output_data_type); } else { @@ -465,14 +440,14 @@ int InstanceNormalizationPlugin::enqueue(const nvinfer1::PluginTensorDesc* input return 0; } -size_t InstanceNormalizationPlugin::getSerializationSize() const +size_t InstanceNormalizationPlugin::getSerializationSize() const noexcept { return (serialized_size(mEpsilon) + serialized_size(mNchan) + serialized_size(mHostScale) + serialized_size(mHostBias) + serialized_size(mRelu) + serialized_size(mAlpha) + serialized_size(mInputScale) + serialized_size(mOutputScale)); } -void InstanceNormalizationPlugin::serialize(void* buffer) const +void InstanceNormalizationPlugin::serialize(void* buffer) const noexcept { serialize_value(&buffer, mEpsilon); serialize_value(&buffer, mNchan); @@ -484,9 +459,8 @@ void InstanceNormalizationPlugin::serialize(void* buffer) const serialize_value(&buffer, mOutputScale); } -// Needs more work bool InstanceNormalizationPlugin::supportsFormatCombination( - int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) + int32_t pos, const nvinfer1::PluginTensorDesc* inOut, int32_t nbInputs, int32_t nbOutputs) noexcept { ASSERT(inOut && pos < (nbInputs + nbOutputs)); @@ -507,22 +481,22 @@ bool InstanceNormalizationPlugin::supportsFormatCombination( return support_fp32_linear || support_fp16_dhwc8 || support_int8_cdhw32; } -const char* InstanceNormalizationPlugin::getPluginType() const +const char* InstanceNormalizationPlugin::getPluginType() const noexcept { return INSTANCE_PLUGIN_NAME; } -const char* InstanceNormalizationPlugin::getPluginVersion() const +const char* InstanceNormalizationPlugin::getPluginVersion() const noexcept { return INSTANCE_PLUGIN_VERSION; } -void InstanceNormalizationPlugin::destroy() +void InstanceNormalizationPlugin::destroy() noexcept { delete this; } -IPluginV2DynamicExt* InstanceNormalizationPlugin::clone() const +IPluginV2DynamicExt* InstanceNormalizationPlugin::clone() const noexcept { auto* plugin = new InstanceNormalizationPlugin{mEpsilon, mHostScale, mHostBias, mRelu, mAlpha}; plugin->setPluginNamespace(mPluginNamespace.c_str()); @@ -531,18 +505,18 @@ IPluginV2DynamicExt* InstanceNormalizationPlugin::clone() const } // Set plugin namespace -void InstanceNormalizationPlugin::setPluginNamespace(const char* pluginNamespace) +void InstanceNormalizationPlugin::setPluginNamespace(const char* pluginNamespace) noexcept { mPluginNamespace = pluginNamespace; } -const char* InstanceNormalizationPlugin::getPluginNamespace() const +const char* InstanceNormalizationPlugin::getPluginNamespace() const noexcept { return mPluginNamespace.c_str(); } nvinfer1::DataType InstanceNormalizationPlugin::getOutputDataType( - int index, const nvinfer1::DataType* inputTypes, int nbInputs) const + int32_t index, const nvinfer1::DataType* inputTypes, int32_t nbInputs) const noexcept { ASSERT(inputTypes && nbInputs > 0 && index == 0); return inputTypes[0]; @@ -550,37 +524,36 @@ nvinfer1::DataType InstanceNormalizationPlugin::getOutputDataType( // Attach the plugin object to an execution context and grant the plugin the access to some context resource. void InstanceNormalizationPlugin::attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) + cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept { } // Detach the plugin object from its execution context. -void InstanceNormalizationPlugin::detachFromContext() {} +void InstanceNormalizationPlugin::detachFromContext() noexcept {} -void InstanceNormalizationPlugin::configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, - const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) +void InstanceNormalizationPlugin::configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int32_t nbInputs, + const nvinfer1::DynamicPluginTensorDesc* out, int32_t nbOutputs) noexcept { - auto input_dims = in[0].desc.dims; - for (int i = 0; i < nbInputs; i++) + auto input_dims = in[0].max; + for (int32_t i = 0; i < nbInputs; i++) { - for (int j = 0; j < input_dims.nbDims; j++) + for (int32_t j = 0; j < input_dims.nbDims; j++) { // Do not support dynamic dimensions ASSERT(input_dims.d[j] != -1); } } - - int n = input_dims.d[0]; - int c = input_dims.d[1]; + int32_t n = input_dims.d[0]; + int32_t c = input_dims.d[1]; size_t nchan_bytes = c * sizeof(float); if (mDeviceBytes < n * nchan_bytes) { - cudaFree(mDeviceBias); - cudaFree(mDeviceScale); + CUASSERT(cudaFree(mDeviceBias)); + CUASSERT(cudaFree(mDeviceScale)); mDeviceBytes = n * nchan_bytes; - cudaMalloc((void**) &mDeviceScale, mDeviceBytes); - cudaMalloc((void**) &mDeviceBias, mDeviceBytes); + CUASSERT(cudaMalloc((void**) &mDeviceScale, mDeviceBytes)); + CUASSERT(cudaMalloc((void**) &mDeviceBias, mDeviceBytes)); } mInputScale = in[0].desc.scale; @@ -600,31 +573,31 @@ InstanceNormalizationPluginCreator::InstanceNormalizationPluginCreator() mFC.fields = mPluginAttributes.data(); } -const char* InstanceNormalizationPluginCreator::getPluginName() const +const char* InstanceNormalizationPluginCreator::getPluginName() const noexcept { return INSTANCE_PLUGIN_NAME; } -const char* InstanceNormalizationPluginCreator::getPluginVersion() const +const char* InstanceNormalizationPluginCreator::getPluginVersion() const noexcept { return INSTANCE_PLUGIN_VERSION; } -const PluginFieldCollection* InstanceNormalizationPluginCreator::getFieldNames() +const PluginFieldCollection* InstanceNormalizationPluginCreator::getFieldNames() noexcept { return &mFC; } IPluginV2DynamicExt* InstanceNormalizationPluginCreator::createPlugin( - const char* name, const nvinfer1::PluginFieldCollection* fc) + const char* name, const nvinfer1::PluginFieldCollection* fc) noexcept { std::vector scaleValues; std::vector biasValues; float epsilon{}; - int relu{}; + int32_t relu{}; float alpha{}; const PluginField* fields = fc->fields; - for (int i = 0; i < fc->nbFields; ++i) + for (int32_t i = 0; i < fc->nbFields; ++i) { const char* attrName = fields[i].name; if (!strcmp(attrName, "epsilon")) @@ -635,10 +608,10 @@ IPluginV2DynamicExt* InstanceNormalizationPluginCreator::createPlugin( else if (!strcmp(attrName, "scales")) { ASSERT(fields[i].type == PluginFieldType::kFLOAT32); - int size = fields[i].length; + int32_t size = fields[i].length; scaleValues.reserve(size); const auto* w = static_cast(fields[i].data); - for (int j = 0; j < size; j++) + for (int32_t j = 0; j < size; j++) { scaleValues.push_back(*w); w++; @@ -647,10 +620,10 @@ IPluginV2DynamicExt* InstanceNormalizationPluginCreator::createPlugin( else if (!strcmp(attrName, "bias")) { ASSERT(fields[i].type == PluginFieldType::kFLOAT32); - int size = fields[i].length; + int32_t size = fields[i].length; biasValues.reserve(size); const auto* w = static_cast(fields[i].data); - for (int j = 0; j < size; j++) + for (int32_t j = 0; j < size; j++) { biasValues.push_back(*w); w++; @@ -659,7 +632,7 @@ IPluginV2DynamicExt* InstanceNormalizationPluginCreator::createPlugin( else if (!strcmp(attrName, "relu")) { ASSERT(fields[i].type == PluginFieldType::kINT32); - relu = *(static_cast(fields[i].data)); + relu = *(static_cast(fields[i].data)); } else if (!strcmp(attrName, "alpha")) { @@ -678,7 +651,7 @@ IPluginV2DynamicExt* InstanceNormalizationPluginCreator::createPlugin( } IPluginV2DynamicExt* InstanceNormalizationPluginCreator::deserializePlugin( - const char* name, const void* serialData, size_t serialLength) + const char* name, const void* serialData, size_t serialLength) noexcept { InstanceNormalizationPlugin* obj = new InstanceNormalizationPlugin{serialData, serialLength}; obj->setPluginNamespace(mNamespace.c_str()); diff --git a/plugin/instanceNormalizationPlugin/instanceNormalizationPlugin.h b/plugin/instanceNormalizationPlugin/instanceNormalizationPlugin.h index a6b874b5..58a78ff6 100644 --- a/plugin/instanceNormalizationPlugin/instanceNormalizationPlugin.h +++ b/plugin/instanceNormalizationPlugin/instanceNormalizationPlugin.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_INSTANCE_NORMALIZATION_PLUGIN_H #define TRT_INSTANCE_NORMALIZATION_PLUGIN_H #include "instanceNormFwd.h" @@ -36,70 +35,72 @@ class InstanceNormalizationPlugin final : public nvinfer1::IPluginV2DynamicExt { public: - InstanceNormalizationPlugin( - float epsilon, nvinfer1::Weights const& scale, nvinfer1::Weights const& bias, int relu = 0, float alpha = 0.f); + InstanceNormalizationPlugin(float epsilon, nvinfer1::Weights const& scale, nvinfer1::Weights const& bias, + int32_t relu = 0, float alpha = 0.f); InstanceNormalizationPlugin(float epsilon, const std::vector& scale, const std::vector& bias, - int relu = 0, float alpha = 0.f); + int32_t relu = 0, float alpha = 0.f); InstanceNormalizationPlugin(void const* serialData, size_t serialLength); InstanceNormalizationPlugin() = delete; ~InstanceNormalizationPlugin() override; - int getNbOutputs() const override; + int32_t getNbOutputs() const noexcept override; // DynamicExt plugins returns DimsExprs class instead of Dims using nvinfer1::IPluginV2::getOutputDimensions; - DimsExprs getOutputDimensions( - int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) override; + DimsExprs getOutputDimensions(int32_t outputIndex, const nvinfer1::DimsExprs* inputs, int32_t nbInputs, + nvinfer1::IExprBuilder& exprBuilder) noexcept override; - int initialize() override; + int32_t initialize() noexcept override; - void terminate() override; + void terminate() noexcept override; using nvinfer1::IPluginV2::getWorkspaceSize; - size_t getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int nbInputs, - const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const override; + size_t getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int32_t nbInputs, + const nvinfer1::PluginTensorDesc* outputs, int32_t nbOutputs) const noexcept override; using nvinfer1::IPluginV2::enqueue; - int enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) override; + int32_t enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; - size_t getSerializationSize() const override; + size_t getSerializationSize() const noexcept override; - void serialize(void* buffer) const override; + void serialize(void* buffer) const noexcept override; // DynamicExt plugin supportsFormat update. bool supportsFormatCombination( - int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) override; + int32_t pos, const nvinfer1::PluginTensorDesc* inOut, int32_t nbInputs, int32_t nbOutputs) noexcept override; - const char* getPluginType() const override; + const char* getPluginType() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - void destroy() override; + void destroy() noexcept override; - nvinfer1::IPluginV2DynamicExt* clone() const override; + nvinfer1::IPluginV2DynamicExt* clone() const noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; - DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + DataType getOutputDataType(int32_t index, const nvinfer1::DataType* inputTypes, int32_t nbInputs) const + noexcept override; - void attachToContext(cudnnContext* cudnn, cublasContext* cublas, nvinfer1::IGpuAllocator* allocator) override; + void attachToContext( + cudnnContext* cudnn, cublasContext* cublas, nvinfer1::IGpuAllocator* allocator) noexcept override; - void detachFromContext() override; + void detachFromContext() noexcept override; using nvinfer1::IPluginV2Ext::configurePlugin; - void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, - const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) override; + void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int32_t nbInputs, + const nvinfer1::DynamicPluginTensorDesc* out, int32_t nbOutputs) noexcept override; private: float mEpsilon; float mAlpha; - int mRelu; - int mNchan; + int32_t mRelu; + int32_t mNchan; std::vector mHostScale; std::vector mHostBias; float* mDeviceScale; @@ -128,15 +129,16 @@ public: ~InstanceNormalizationPluginCreator() override = default; - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const PluginFieldCollection* getFieldNames() override; + const PluginFieldCollection* getFieldNames() noexcept override; - IPluginV2DynamicExt* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) override; + IPluginV2DynamicExt* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) noexcept override; - IPluginV2DynamicExt* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + IPluginV2DynamicExt* deserializePlugin( + const char* name, const void* serialData, size_t serialLength) noexcept override; private: static PluginFieldCollection mFC; diff --git a/plugin/leakyReluPlugin/lReluPlugin.cpp b/plugin/leakyReluPlugin/lReluPlugin.cpp index 6d3bd726..22596c02 100644 --- a/plugin/leakyReluPlugin/lReluPlugin.cpp +++ b/plugin/leakyReluPlugin/lReluPlugin.cpp @@ -13,13 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "lReluPlugin.h" #include "checkMacrosPlugin.h" #include "kernel.h" using namespace nvinfer1; -using nvinfer1::PluginType; using nvinfer1::plugin::LReluPluginCreator; using nvinfer1::plugin::LReLU; @@ -37,51 +35,51 @@ LReLU::LReLU(float negSlope) LReLU::LReLU(const void* buffer, size_t length) { - const char *d = reinterpret_cast(buffer), *a = d; + const char *d = reinterpret_cast(buffer), *a = d; mNegSlope = read(d); mBatchDim = read(d); ASSERT(d == a + length); } -int LReLU::getNbOutputs() const +int LReLU::getNbOutputs() const noexcept { return 1; } -Dims LReLU::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) +Dims LReLU::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept { ASSERT(nbInputDims == 1); ASSERT(index == 0); return inputs[0]; } -int LReLU::enqueue(int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) +int LReLU::enqueue( + int batchSize, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { const void* inputData = inputs[0]; void* outputData = outputs[0]; pluginStatus_t status = lReLUInference(stream, mBatchDim * batchSize, mNegSlope, inputData, outputData); - ASSERT(status == STATUS_SUCCESS); return status; } -size_t LReLU::getSerializationSize() const +size_t LReLU::getSerializationSize() const noexcept { // mNegSlope, mBatchDim return sizeof(float) + sizeof(int); } -void LReLU::serialize(void* buffer) const +void LReLU::serialize(void* buffer) const noexcept { - char *d = reinterpret_cast(buffer), *a = d; + char *d = reinterpret_cast(buffer), *a = d; write(d, mNegSlope); write(d, mBatchDim); ASSERT(d == a + getSerializationSize()); } void LReLU::configureWithFormat( - const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, DataType type, PluginFormat format, int) + const Dims* inputDims, int /* nbInputs */, const Dims* /* outputDims */, int nbOutputs, DataType type, PluginFormat format, int) noexcept { - ASSERT(type == DataType::kFLOAT && format == PluginFormat::kNCHW); + ASSERT(type == DataType::kFLOAT && format == PluginFormat::kLINEAR); ASSERT(mBatchDim == 1); ASSERT(nbOutputs == 1); for (int i = 0; i < inputDims[0].nbDims; ++i) @@ -90,39 +88,39 @@ void LReLU::configureWithFormat( } } -bool LReLU::supportsFormat(DataType type, PluginFormat format) const +bool LReLU::supportsFormat(DataType type, PluginFormat format) const noexcept { - return (type == DataType::kFLOAT && format == PluginFormat::kNCHW); + return (type == DataType::kFLOAT && format == PluginFormat::kLINEAR); } -int LReLU::initialize() +int LReLU::initialize() noexcept { return 0; } -void LReLU::terminate() {} +void LReLU::terminate() noexcept {} -size_t LReLU::getWorkspaceSize(int maxBatchSize) const +size_t LReLU::getWorkspaceSize(int /* maxBatchSize */) const noexcept { return 0; } -const char* LReLU::getPluginType() const +const char* LReLU::getPluginType() const noexcept { return LRELU_PLUGIN_NAME; } -const char* LReLU::getPluginVersion() const +const char* LReLU::getPluginVersion() const noexcept { return LRELU_PLUGIN_VERSION; } -void LReLU::destroy() +void LReLU::destroy() noexcept { delete this; } -IPluginV2* LReLU::clone() const +IPluginV2* LReLU::clone() const noexcept { IPluginV2* plugin = new LReLU(mNegSlope); plugin->setPluginNamespace(mNamespace.c_str()); @@ -137,32 +135,32 @@ LReluPluginCreator::LReluPluginCreator() mFC.fields = mPluginAttributes.data(); } -const char* LReluPluginCreator::getPluginName() const +const char* LReluPluginCreator::getPluginName() const noexcept { return LRELU_PLUGIN_NAME; } -const char* LReluPluginCreator::getPluginVersion() const +const char* LReluPluginCreator::getPluginVersion() const noexcept { return LRELU_PLUGIN_VERSION; } -const PluginFieldCollection* LReluPluginCreator::getFieldNames() +const PluginFieldCollection* LReluPluginCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2* LReluPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2* LReluPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { const PluginField* fields = fc->fields; ASSERT(fc->nbFields == 1); ASSERT(fields[0].type == PluginFieldType::kFLOAT32); - negSlope = *(static_cast(fields[0].data)); + float negSlope = *(static_cast(fields[0].data)); return new LReLU(negSlope); } -IPluginV2* LReluPluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) +IPluginV2* LReluPluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept { // This object will be deleted when the network is destroyed, which will // call LReluPlugin::destroy() diff --git a/plugin/leakyReluPlugin/lReluPlugin.h b/plugin/leakyReluPlugin/lReluPlugin.h index 0c7086ff..693cd8b7 100644 --- a/plugin/leakyReluPlugin/lReluPlugin.h +++ b/plugin/leakyReluPlugin/lReluPlugin.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_L_RELU_PLUGIN_H #define TRT_L_RELU_PLUGIN_H #include "NvInfer.h" @@ -38,35 +37,34 @@ public: ~LReLU() override = default; - int getNbOutputs() const override; + int getNbOutputs() const noexcept override; - Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) override; + Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept override; - int initialize() override; + int initialize() noexcept override; - void terminate() override; + void terminate() noexcept override; - size_t getWorkspaceSize(int maxBatchSize) const override; + size_t getWorkspaceSize(int maxBatchSize) const noexcept override; - int enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) override; + int enqueue(int batchSize, const void* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept override; - size_t getSerializationSize() const override; + size_t getSerializationSize() const noexcept override; - void serialize(void* buffer) const override; + void serialize(void* buffer) const noexcept override; - void configureWithFormat(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, DataType type, - PluginFormat format, int maxBatchSize) override; + void configureWithFormat(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, DataType type, PluginFormat format, int maxBatchSize) noexcept override; - bool supportsFormat(DataType type, PluginFormat format) const override; + bool supportsFormat(DataType type, PluginFormat format) const noexcept override; - const char* getPluginType() const override; + const char* getPluginType() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - void destroy() override; + void destroy() noexcept override; - IPluginV2* clone() const override; + IPluginV2* clone() const noexcept override; private: float mNegSlope; @@ -80,19 +78,18 @@ public: ~LReluPluginCreator() override = default; - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const PluginFieldCollection* getFieldNames() override; + const PluginFieldCollection* getFieldNames() noexcept override; - IPluginV2* createPlugin(const char* name, const PluginFieldCollection* fc) override; + IPluginV2* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; - IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; private: static PluginFieldCollection mFC; - float negSlope{}; static std::vector mPluginAttributes; }; diff --git a/plugin/multilevelCropAndResizePlugin/multilevelCropAndResizePlugin.cpp b/plugin/multilevelCropAndResizePlugin/multilevelCropAndResizePlugin.cpp index 1b9fea3d..77e1105d 100644 --- a/plugin/multilevelCropAndResizePlugin/multilevelCropAndResizePlugin.cpp +++ b/plugin/multilevelCropAndResizePlugin/multilevelCropAndResizePlugin.cpp @@ -47,17 +47,17 @@ MultilevelCropAndResizePluginCreator::MultilevelCropAndResizePluginCreator() noe const char* MultilevelCropAndResizePluginCreator::getPluginName() const noexcept { return MULTILEVELCROPANDRESIZE_PLUGIN_NAME; -}; +} const char* MultilevelCropAndResizePluginCreator::getPluginVersion() const noexcept { return MULTILEVELCROPANDRESIZE_PLUGIN_VERSION; -}; +} const PluginFieldCollection* MultilevelCropAndResizePluginCreator::getFieldNames() noexcept { return &mFC; -}; +} IPluginV2Ext* MultilevelCropAndResizePluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { @@ -79,12 +79,12 @@ IPluginV2Ext* MultilevelCropAndResizePluginCreator::createPlugin(const char* nam } } return new MultilevelCropAndResize(mPooledSize, image_size); -}; +} IPluginV2Ext* MultilevelCropAndResizePluginCreator::deserializePlugin(const char* name, const void* data, size_t length) noexcept { return new MultilevelCropAndResize(data, length); -}; +} MultilevelCropAndResize::MultilevelCropAndResize(int pooled_size, const nvinfer1::Dims& image_size) noexcept : mPooledSize({pooled_size, pooled_size}) @@ -96,26 +96,26 @@ MultilevelCropAndResize::MultilevelCropAndResize(int pooled_size, const nvinfer1 mInputWidth = image_size.d[2]; // Threshold to P3: Smaller -> P2 mThresh = (224 * 224) / (4.0f); -}; +} int MultilevelCropAndResize::getNbOutputs() const noexcept { return 1; -}; +} int MultilevelCropAndResize::initialize() noexcept { return 0; -}; +} -void MultilevelCropAndResize::terminate() noexcept { - -}; +void MultilevelCropAndResize::terminate() noexcept +{ +} void MultilevelCropAndResize::destroy() noexcept { delete this; -}; +} size_t MultilevelCropAndResize::getWorkspaceSize(int) const noexcept { @@ -124,28 +124,28 @@ size_t MultilevelCropAndResize::getWorkspaceSize(int) const noexcept bool MultilevelCropAndResize::supportsFormat(DataType type, PluginFormat format) const noexcept { - return ((type == DataType::kFLOAT || type == DataType::kHALF) && format == PluginFormat::kNCHW); -}; + return ((type == DataType::kFLOAT || type == DataType::kHALF) && format == PluginFormat::kLINEAR); +} const char* MultilevelCropAndResize::getPluginType() const noexcept { return "MultilevelCropAndResize_TRT"; -}; +} const char* MultilevelCropAndResize::getPluginVersion() const noexcept { return "1"; -}; +} IPluginV2Ext* MultilevelCropAndResize::clone() const noexcept { return new MultilevelCropAndResize(*this); -}; +} void MultilevelCropAndResize::setPluginNamespace(const char* libNamespace) noexcept { mNameSpace = libNamespace; -}; +} const char* MultilevelCropAndResize::getPluginNamespace() const noexcept { @@ -191,10 +191,10 @@ Dims MultilevelCropAndResize::getOutputDimensions(int index, const Dims* inputs, result.d[3] = mPooledSize.x; return result; -}; +} -int MultilevelCropAndResize::enqueue( - int batch_size, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) noexcept +int32_t MultilevelCropAndResize::enqueue( + int32_t batch_size, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { void* pooled = outputs[0]; @@ -207,12 +207,12 @@ int MultilevelCropAndResize::enqueue( assert(status == cudaSuccess); return 0; -}; +} size_t MultilevelCropAndResize::getSerializationSize() const noexcept { return sizeof(int) * 2 + sizeof(int) * 4 + sizeof(float) + sizeof(int) * 2 * mFeatureMapCount + sizeof(DataType); -}; +} void MultilevelCropAndResize::serialize(void* buffer) const noexcept { @@ -231,7 +231,7 @@ void MultilevelCropAndResize::serialize(void* buffer) const noexcept } write(d, mPrecision); assert(d == a + getSerializationSize()); -}; +} MultilevelCropAndResize::MultilevelCropAndResize(const void* data, size_t length) noexcept { @@ -250,7 +250,7 @@ MultilevelCropAndResize::MultilevelCropAndResize(const void* data, size_t length mPrecision = read(d); assert(d == a + length); -}; +} // Return the DataType of the plugin output at the requested index DataType MultilevelCropAndResize::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept @@ -306,4 +306,6 @@ void MultilevelCropAndResize::attachToContext( } // Detach the plugin object from its execution context. -void MultilevelCropAndResize::detachFromContext() noexcept {} +void MultilevelCropAndResize::detachFromContext() noexcept +{ +} diff --git a/plugin/multilevelCropAndResizePlugin/multilevelCropAndResizePlugin.h b/plugin/multilevelCropAndResizePlugin/multilevelCropAndResizePlugin.h index 4f460252..0a9fdd3f 100644 --- a/plugin/multilevelCropAndResizePlugin/multilevelCropAndResizePlugin.h +++ b/plugin/multilevelCropAndResizePlugin/multilevelCropAndResizePlugin.h @@ -54,8 +54,8 @@ public: size_t getWorkspaceSize(int) const noexcept override; - int enqueue( - int batch_size, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) noexcept override; + int32_t enqueue( + int32_t batch_size, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; size_t getSerializationSize() const noexcept override; diff --git a/plugin/multilevelProposeROI/multilevelProposeROIPlugin.cpp b/plugin/multilevelProposeROI/multilevelProposeROIPlugin.cpp index f6b2d2cf..139f6f6f 100644 --- a/plugin/multilevelProposeROI/multilevelProposeROIPlugin.cpp +++ b/plugin/multilevelProposeROI/multilevelProposeROIPlugin.cpp @@ -54,17 +54,17 @@ MultilevelProposeROIPluginCreator::MultilevelProposeROIPluginCreator() noexcept const char* MultilevelProposeROIPluginCreator::getPluginName() const noexcept { return MULTILEVELPROPOSEROI_PLUGIN_NAME; -}; +} const char* MultilevelProposeROIPluginCreator::getPluginVersion() const noexcept { return MULTILEVELPROPOSEROI_PLUGIN_VERSION; -}; +} const PluginFieldCollection* MultilevelProposeROIPluginCreator::getFieldNames() noexcept { return &mFC; -}; +} IPluginV2Ext* MultilevelProposeROIPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { @@ -101,12 +101,12 @@ IPluginV2Ext* MultilevelProposeROIPluginCreator::createPlugin(const char* name, } } return new MultilevelProposeROI(mPreNMSTopK, mKeepTopK, mFGThreshold, mIOUThreshold, image_size); -}; +} IPluginV2Ext* MultilevelProposeROIPluginCreator::deserializePlugin(const char* name, const void* data, size_t length) noexcept { return new MultilevelProposeROI(data, length); -}; +} MultilevelProposeROI::MultilevelProposeROI( int prenms_topk, int keep_topk, float fg_threshold, float iou_threshold, const nvinfer1::Dims image_size) noexcept @@ -133,12 +133,12 @@ MultilevelProposeROI::MultilevelProposeROI( mFeatureCnt = TLTMaskRCNNConfig::MAX_LEVEL - TLTMaskRCNNConfig::MIN_LEVEL + 1; generate_pyramid_anchors(mImageSize); -}; +} int MultilevelProposeROI::getNbOutputs() const noexcept { return 1; -}; +} int MultilevelProposeROI::initialize() noexcept { @@ -205,49 +205,51 @@ int MultilevelProposeROI::initialize() noexcept CUASSERT(cudaMemcpy(mDeviceBboxes, box_tp.data(), sizeof(void*) * mFeatureCnt, cudaMemcpyHostToDevice)); return 0; -}; +} -void MultilevelProposeROI::terminate() noexcept {}; +void MultilevelProposeROI::terminate() noexcept +{ +} void MultilevelProposeROI::destroy() noexcept { delete this; -}; +} bool MultilevelProposeROI::supportsFormat(DataType type, PluginFormat format) const noexcept { - return ((type == DataType::kFLOAT || type == DataType::kHALF) && format == PluginFormat::kNCHW); -}; + return ((type == DataType::kFLOAT || type == DataType::kHALF) && format == PluginFormat::kLINEAR); +} const char* MultilevelProposeROI::getPluginType() const noexcept { return "MultilevelProposeROI_TRT"; -}; +} const char* MultilevelProposeROI::getPluginVersion() const noexcept { return "1"; -}; +} IPluginV2Ext* MultilevelProposeROI::clone() const noexcept { return new MultilevelProposeROI(*this); -}; +} void MultilevelProposeROI::setPluginNamespace(const char* libNamespace) noexcept { mNameSpace = libNamespace; -}; +} const char* MultilevelProposeROI::getPluginNamespace() const noexcept { return mNameSpace.c_str(); -}; +} size_t MultilevelProposeROI::getSerializationSize() const noexcept { return sizeof(int) * 2 + sizeof(float) * 2 + sizeof(int) * (mFeatureCnt + 1) + sizeof(nvinfer1::Dims) + sizeof(DataType); -}; +} void MultilevelProposeROI::serialize(void* buffer) const noexcept { @@ -264,7 +266,7 @@ void MultilevelProposeROI::serialize(void* buffer) const noexcept write(d, mImageSize); write(d, mType); ASSERT(d == a + getSerializationSize()); -}; +} MultilevelProposeROI::MultilevelProposeROI(const void* data, size_t length) noexcept { @@ -298,7 +300,7 @@ MultilevelProposeROI::MultilevelProposeROI(const void* data, size_t length) noex mParam.iouThreshold = mIOUThreshold; generate_pyramid_anchors(mImageSize); -}; +} void MultilevelProposeROI::check_valid_inputs(const nvinfer1::Dims* inputs, int nbInputDims) noexcept { @@ -314,7 +316,7 @@ void MultilevelProposeROI::check_valid_inputs(const nvinfer1::Dims* inputs, int // foreground_score assert(inputs[i + 1].nbDims == 3 && inputs[i + 1].d[1] == 1); } -}; +} size_t MultilevelProposeROI::getWorkspaceSize(int batch_size) const noexcept { @@ -334,7 +336,7 @@ size_t MultilevelProposeROI::getWorkspaceSize(int batch_size) const noexcept total_size += ct.totalSize; return total_size; -}; +} Dims MultilevelProposeROI::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept { @@ -399,8 +401,8 @@ void MultilevelProposeROI::generate_pyramid_anchors(const nvinfer1::Dims& image_ assert(anchors.size() == (max_level - min_level + 1)); } -int MultilevelProposeROI::enqueue( - int batch_size, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) noexcept +int32_t MultilevelProposeROI::enqueue( + int32_t batch_size, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { void* final_proposals = outputs[0]; @@ -450,7 +452,7 @@ int MultilevelProposeROI::enqueue( assert(status == cudaSuccess); return status; -}; +} // Return the DataType of the plugin output at the requested index DataType MultilevelProposeROI::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept @@ -500,4 +502,6 @@ void MultilevelProposeROI::attachToContext( } // Detach the plugin object from its execution context. -void MultilevelProposeROI::detachFromContext() noexcept {} +void MultilevelProposeROI::detachFromContext() noexcept +{ +} diff --git a/plugin/multilevelProposeROI/multilevelProposeROIPlugin.h b/plugin/multilevelProposeROI/multilevelProposeROIPlugin.h index d0fa77cc..558ece26 100644 --- a/plugin/multilevelProposeROI/multilevelProposeROIPlugin.h +++ b/plugin/multilevelProposeROI/multilevelProposeROIPlugin.h @@ -54,8 +54,8 @@ public: size_t getWorkspaceSize(int maxBatchSize) const noexcept override; - int enqueue( - int batch_size, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) noexcept override; + int32_t enqueue( + int32_t batch_size, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; size_t getSerializationSize() const noexcept override; diff --git a/plugin/nmsPlugin/nmsPlugin.cpp b/plugin/nmsPlugin/nmsPlugin.cpp index bd8924a6..cc1944a6 100644 --- a/plugin/nmsPlugin/nmsPlugin.cpp +++ b/plugin/nmsPlugin/nmsPlugin.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "nmsPlugin.h" #include #include @@ -38,34 +37,48 @@ PluginFieldCollection NMSBasePluginCreator::mFC{}; std::vector NMSBasePluginCreator::mPluginAttributes; // Constrcutor -DetectionOutput::DetectionOutput(DetectionOutputParameters params) noexcept +DetectionOutput::DetectionOutput(DetectionOutputParameters params) : param(params) + , C1(0) + , C2(0) + , numPriors(0) + , mType(DataType::kFLOAT) + , mScoreBits(16) { } -DetectionOutputDynamic::DetectionOutputDynamic(DetectionOutputParameters params) noexcept +DetectionOutputDynamic::DetectionOutputDynamic(DetectionOutputParameters params) : param(params) + , C1(0) + , C2(0) + , numPriors(0) + , mType(DataType::kFLOAT) + , mScoreBits(16) { } -DetectionOutput::DetectionOutput(DetectionOutputParameters params, int C1, int C2, int numPriors) noexcept +DetectionOutput::DetectionOutput(DetectionOutputParameters params, int C1, int C2, int numPriors) : param(params) , C1(C1) , C2(C2) , numPriors(numPriors) + , mType(DataType::kFLOAT) + , mScoreBits(16) { } -DetectionOutputDynamic::DetectionOutputDynamic(DetectionOutputParameters params, int C1, int C2, int numPriors) noexcept +DetectionOutputDynamic::DetectionOutputDynamic(DetectionOutputParameters params, int C1, int C2, int numPriors) : param(params) , C1(C1) , C2(C2) , numPriors(numPriors) + , mType(DataType::kFLOAT) + , mScoreBits(16) { } // Parameterized constructor -DetectionOutput::DetectionOutput(const void* data, size_t length) noexcept +DetectionOutput::DetectionOutput(const void* data, size_t length) { const char *d = reinterpret_cast(data), *a = d; param = read(d); @@ -84,7 +97,7 @@ DetectionOutput::DetectionOutput(const void* data, size_t length) noexcept ASSERT(d == a + length); } -DetectionOutputDynamic::DetectionOutputDynamic(const void* data, size_t length) noexcept +DetectionOutputDynamic::DetectionOutputDynamic(const void* data, size_t length) { const char *d = reinterpret_cast(data), *a = d; param = read(d); @@ -139,9 +152,9 @@ Dims DetectionOutput::getOutputDimensions(int index, const Dims* inputs, int nbI // index 1: Dimensions 1x1x1 if (index == 0) { - return DimsCHW(1, param.keepTopK, 7); + return Dims3(1, param.keepTopK, 7); } - return DimsCHW(1, 1, 1); + return Dims3(1, 1, 1); } DimsExprs DetectionOutputDynamic::getOutputDimensions( @@ -212,7 +225,7 @@ size_t DetectionOutputDynamic::getWorkspaceSize( // Plugin layer implementation int DetectionOutput::enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) noexcept + int batchSize, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { // Input order {loc, conf, prior} const void* const locData = inputs[param.inputOrder[0]]; @@ -226,12 +239,11 @@ int DetectionOutput::enqueue( pluginStatus_t status = detectionInference(stream, batchSize, C1, C2, param.shareLocation, param.varianceEncodedInTarget, param.backgroundLabelId, numPriors, param.numClasses, param.topK, param.keepTopK, param.confidenceThreshold, param.nmsThreshold, param.codeType, mType, locData, priorData, mType, confData, - keepCount, topDetections, workspace, param.isNormalized, param.confSigmoid, mScoreBits); - ASSERT(status == STATUS_SUCCESS); - return 0; + keepCount, topDetections, workspace, param.isNormalized, param.confSigmoid, mScoreBits, param.isBatchAgnostic); + return status; } -int DetectionOutputDynamic::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, +int32_t DetectionOutputDynamic::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { // Input order {loc, conf, prior} @@ -246,9 +258,8 @@ int DetectionOutputDynamic::enqueue(const PluginTensorDesc* inputDesc, const Plu pluginStatus_t status = detectionInference(stream, inputDesc[0].dims.d[0], C1, C2, param.shareLocation, param.varianceEncodedInTarget, param.backgroundLabelId, numPriors, param.numClasses, param.topK, param.keepTopK, param.confidenceThreshold, param.nmsThreshold, param.codeType, mType, locData, priorData, mType, confData, - keepCount, topDetections, workspace, param.isNormalized, param.confSigmoid, mScoreBits); - ASSERT(status == STATUS_SUCCESS); - return 0; + keepCount, topDetections, workspace, param.isNormalized, param.confSigmoid, mScoreBits, false); + return status; } // Returns the size of serialized parameters @@ -292,7 +303,7 @@ void DetectionOutputDynamic::serialize(void* buffer) const noexcept // Check if the DataType and Plugin format is supported bool DetectionOutput::supportsFormat(DataType type, PluginFormat format) const noexcept { - return ((type == DataType::kHALF || type == DataType::kFLOAT) && format == PluginFormat::kNCHW); + return ((type == DataType::kHALF || type == DataType::kFLOAT) && format == PluginFormat::kLINEAR); } bool DetectionOutputDynamic::supportsFormatCombination( @@ -412,8 +423,7 @@ const char* DetectionOutputDynamic::getPluginNamespace() const noexcept } // Return the DataType of the plugin output at the requested index. -DataType DetectionOutput::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const - noexcept +DataType DetectionOutput::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { // Two outputs ASSERT(index == 0 || index == 1); @@ -428,8 +438,7 @@ DataType DetectionOutput::getOutputDataType(int index, const nvinfer1::DataType* return DataType::kFLOAT; } -DataType DetectionOutputDynamic::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const - noexcept +DataType DetectionOutputDynamic::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { // Two outputs ASSERT(index == 0 || index == 1); @@ -445,8 +454,7 @@ DataType DetectionOutputDynamic::getOutputDataType(int index, const nvinfer1::Da } // Return true if output tensor is broadcast across a batch. -bool DetectionOutput::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const - noexcept +bool DetectionOutput::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept { return false; } @@ -550,7 +558,7 @@ void DetectionOutput::attachToContext( void DetectionOutput::detachFromContext() noexcept {} // Plugin creator constructor -NMSBasePluginCreator::NMSBasePluginCreator() noexcept +NMSBasePluginCreator::NMSBasePluginCreator() { // NMS Plugin field meta data {name, data, type, length} mPluginAttributes.clear(); @@ -572,12 +580,12 @@ NMSBasePluginCreator::NMSBasePluginCreator() noexcept mFC.fields = mPluginAttributes.data(); } -NMSPluginCreator::NMSPluginCreator() noexcept +NMSPluginCreator::NMSPluginCreator() { mPluginName = NMS_PLUGIN_NAMES[0]; } -NMSDynamicPluginCreator::NMSDynamicPluginCreator() noexcept +NMSDynamicPluginCreator::NMSDynamicPluginCreator() { mPluginName = NMS_PLUGIN_NAMES[1]; } @@ -790,8 +798,7 @@ IPluginV2DynamicExt* NMSDynamicPluginCreator::createPlugin(const char* name, con return obj; } -IPluginV2Ext* NMSPluginCreator::deserializePlugin( - const char* name, const void* serialData, size_t serialLength) noexcept +IPluginV2Ext* NMSPluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept { // This object will be deleted when the network is destroyed, which will // call NMS::destroy() @@ -800,8 +807,7 @@ IPluginV2Ext* NMSPluginCreator::deserializePlugin( return obj; } -IPluginV2DynamicExt* NMSDynamicPluginCreator::deserializePlugin( - const char* name, const void* serialData, size_t serialLength) noexcept +IPluginV2DynamicExt* NMSDynamicPluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept { // This object will be deleted when the network is destroyed, which will // call NMS::destroy() diff --git a/plugin/nmsPlugin/nmsPlugin.h b/plugin/nmsPlugin/nmsPlugin.h index 9b737345..35b9d756 100644 --- a/plugin/nmsPlugin/nmsPlugin.h +++ b/plugin/nmsPlugin/nmsPlugin.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_NMS_PLUGIN_H #define TRT_NMS_PLUGIN_H #include "kernel.h" @@ -31,13 +30,13 @@ namespace plugin class DetectionOutput : public IPluginV2Ext { public: - DetectionOutput(DetectionOutputParameters param) noexcept; + DetectionOutput(DetectionOutputParameters param); - DetectionOutput(DetectionOutputParameters param, int C1, int C2, int numPriors) noexcept; + DetectionOutput(DetectionOutputParameters param, int C1, int C2, int numPriors); - DetectionOutput(const void* data, size_t length) noexcept; + DetectionOutput(const void* data, size_t length); - ~DetectionOutput() noexcept override = default; + ~DetectionOutput() override = default; int getNbOutputs() const noexcept override; @@ -49,7 +48,7 @@ public: size_t getWorkspaceSize(int maxBatchSize) const noexcept override; - int enqueue(int batchSize, const void* const* inputs, void** outputs, void* workspace, + int enqueue(int batchSize, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; size_t getSerializationSize() const noexcept override; @@ -72,8 +71,7 @@ public: DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept override; - bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const - noexcept override; + bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept override; bool canBroadcastInputAcrossBatch(int inputIndex) const noexcept override; @@ -99,10 +97,10 @@ private: class DetectionOutputDynamic : public IPluginV2DynamicExt { public: - DetectionOutputDynamic(DetectionOutputParameters param) noexcept; - DetectionOutputDynamic(DetectionOutputParameters param, int C1, int C2, int numPriors) noexcept; - DetectionOutputDynamic(const void* data, size_t length) noexcept; - ~DetectionOutputDynamic() noexcept override = default; + DetectionOutputDynamic(DetectionOutputParameters param); + DetectionOutputDynamic(DetectionOutputParameters param, int C1, int C2, int numPriors); + DetectionOutputDynamic(const void* data, size_t length); + ~DetectionOutputDynamic() override = default; // IPluginV2 methods const char* getPluginType() const noexcept override; @@ -130,7 +128,7 @@ public: int nbOutputs) noexcept override; size_t getWorkspaceSize(const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const noexcept override; - int enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, const void* const* inputs, + int32_t enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; private: @@ -144,8 +142,8 @@ private: class NMSBasePluginCreator : public BaseCreator { public: - NMSBasePluginCreator() noexcept; - ~NMSBasePluginCreator() noexcept override = default; + NMSBasePluginCreator(); + ~NMSBasePluginCreator() override = default; const char* getPluginName() const noexcept override; const char* getPluginVersion() const noexcept override; const PluginFieldCollection* getFieldNames() noexcept override; @@ -162,8 +160,8 @@ protected: class NMSPluginCreator : public NMSBasePluginCreator { public: - NMSPluginCreator() noexcept; - ~NMSPluginCreator() noexcept override = default; + NMSPluginCreator(); + ~NMSPluginCreator() override = default; IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; }; @@ -171,8 +169,8 @@ public: class NMSDynamicPluginCreator : public NMSBasePluginCreator { public: - NMSDynamicPluginCreator() noexcept; - ~NMSDynamicPluginCreator() noexcept override = default; + NMSDynamicPluginCreator(); + ~NMSDynamicPluginCreator() override = default; IPluginV2DynamicExt* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; IPluginV2DynamicExt* deserializePlugin( const char* name, const void* serialData, size_t serialLength) noexcept override; diff --git a/plugin/normalizePlugin/normalizePlugin.cpp b/plugin/normalizePlugin/normalizePlugin.cpp index 2f879ab9..ae294139 100644 --- a/plugin/normalizePlugin/normalizePlugin.cpp +++ b/plugin/normalizePlugin/normalizePlugin.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "normalizePlugin.h" #include "half.h" #include @@ -63,8 +62,8 @@ Normalize::Normalize( Normalize::Normalize(const void* buffer, size_t length) { - const char* d = static_cast(buffer); - const char* a = d; + const char *d = static_cast(buffer); + const char *a = d; C = read(d); H = read(d); W = read(d); @@ -78,49 +77,52 @@ Normalize::Normalize(const void* buffer, size_t length) ASSERT(d == a + length); } -int Normalize::getNbOutputs() const +int Normalize::getNbOutputs() const noexcept { // Plugin layer has 1 output return 1; } -Dims Normalize::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) +Dims Normalize::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept { ASSERT(nbInputDims == 1); ASSERT(index == 0); ASSERT(inputs[0].nbDims == 3); - return DimsCHW(inputs[0].d[0], inputs[0].d[1], inputs[0].d[2]); + return Dims3(inputs[0].d[0], inputs[0].d[1], inputs[0].d[2]); } -int Normalize::initialize() +int Normalize::initialize() noexcept { - return 0; + return STATUS_SUCCESS; } -void Normalize::terminate() {} +void Normalize::terminate() noexcept +{ +} -size_t Normalize::getWorkspaceSize(int maxBatchSize) const +size_t Normalize::getWorkspaceSize(int maxBatchSize) const noexcept { return normalizePluginWorkspaceSize(acrossSpatial, C, H, W); } -int Normalize::enqueue(int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) +int Normalize::enqueue( + int batchSize, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { const void* inputData = inputs[0]; void* outputData = outputs[0]; pluginStatus_t status = normalizeInference(stream, mCublas, acrossSpatial, channelShared, batchSize, C, H, W, eps, static_cast(mWeights.values), inputData, outputData, workspace); - ASSERT(status == STATUS_SUCCESS); - return 0; + + return status; } -size_t Normalize::getSerializationSize() const +size_t Normalize::getSerializationSize() const noexcept { // C,H,W, acrossSpatial,channelShared, eps, mWeights.count,mWeights.values return sizeof(int) * 3 + sizeof(bool) * 2 + sizeof(float) + sizeof(int) * 2 + mWeights.count * sizeof(float); } -void Normalize::serialize(void* buffer) const +void Normalize::serialize(void* buffer) const noexcept { char *d = static_cast(buffer), *a = d; write(d, C); @@ -136,9 +138,9 @@ void Normalize::serialize(void* buffer) const ASSERT(d == a + getSerializationSize()); } -bool Normalize::supportsFormat(DataType type, PluginFormat format) const +bool Normalize::supportsFormat(DataType type, PluginFormat format) const noexcept { - return (type == DataType::kFLOAT && format == PluginFormat::kNCHW); + return (type == DataType::kFLOAT && format == PluginFormat::kLINEAR); } Weights Normalize::copyToDevice(const void* hostData, size_t count) @@ -163,31 +165,31 @@ Weights Normalize::deserializeToDevice(const char*& hostBuffer, size_t count) } // Set plugin namespace -void Normalize::setPluginNamespace(const char* pluginNamespace) +void Normalize::setPluginNamespace(const char* pluginNamespace) noexcept { mPluginNamespace = pluginNamespace; } -const char* Normalize::getPluginNamespace() const +const char* Normalize::getPluginNamespace() const noexcept { return mPluginNamespace.c_str(); } // Return the DataType of the plugin output at the requested index -DataType Normalize::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const +DataType Normalize::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { ASSERT(index == 0); return DataType::kFLOAT; } // Return true if output tensor is broadcast across a batch. -bool Normalize::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const +bool Normalize::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept { return false; } // Return true if plugin can use input that is broadcast across batch without replication. -bool Normalize::canBroadcastInputAcrossBatch(int inputIndex) const +bool Normalize::canBroadcastInputAcrossBatch(int inputIndex) const noexcept { return false; } @@ -195,9 +197,9 @@ bool Normalize::canBroadcastInputAcrossBatch(int inputIndex) const // Configure the layer with input and output data types. void Normalize::configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept { - ASSERT(*inputTypes == DataType::kFLOAT && floatFormat == PluginFormat::kNCHW); + ASSERT(*inputTypes == DataType::kFLOAT && floatFormat == PluginFormat::kLINEAR); C = inputDims[0].d[0]; H = inputDims[0].d[1]; W = inputDims[0].d[2]; @@ -218,31 +220,34 @@ void Normalize::configurePlugin(const Dims* inputDims, int nbInputs, const Dims* } // Attach the plugin object to an execution context and grant the plugin the access to some context resource. -void Normalize::attachToContext(cudnnContext* cudnn, cublasContext* cublas, IGpuAllocator* gpuAllocator) +void Normalize::attachToContext(cudnnContext* cudnn, cublasContext* cublas, IGpuAllocator* gpuAllocator) noexcept { mCublas = cublas; } // Detach the plugin object from its execution context. -void Normalize::detachFromContext() {} +void Normalize::detachFromContext() noexcept +{ +} -const char* Normalize::getPluginType() const +const char* Normalize::getPluginType() const noexcept { return NORMALIZE_PLUGIN_NAME; } -const char* Normalize::getPluginVersion() const +const char* Normalize::getPluginVersion() const noexcept { return NORMALIZE_PLUGIN_VERSION; } -void Normalize::destroy() +void Normalize::destroy() noexcept { + CUASSERT(cudaFree(const_cast(mWeights.values))); delete this; } // Clone the plugin -IPluginV2Ext* Normalize::clone() const +IPluginV2Ext* Normalize::clone() const noexcept { // Create a new instance IPluginV2Ext* plugin = new Normalize(&mWeights, mNbWeights, acrossSpatial, channelShared, eps, C, H, W); @@ -264,22 +269,22 @@ NormalizePluginCreator::NormalizePluginCreator() mFC.fields = mPluginAttributes.data(); } -const char* NormalizePluginCreator::getPluginName() const +const char* NormalizePluginCreator::getPluginName() const noexcept { return NORMALIZE_PLUGIN_NAME; } -const char* NormalizePluginCreator::getPluginVersion() const +const char* NormalizePluginCreator::getPluginVersion() const noexcept { return NORMALIZE_PLUGIN_VERSION; } -const PluginFieldCollection* NormalizePluginCreator::getFieldNames() +const PluginFieldCollection* NormalizePluginCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2Ext* NormalizePluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2Ext* NormalizePluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { std::vector weightValues; const PluginField* fields = fc->fields; @@ -326,7 +331,7 @@ IPluginV2Ext* NormalizePluginCreator::createPlugin(const char* name, const Plugi return obj; } -IPluginV2Ext* NormalizePluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) +IPluginV2Ext* NormalizePluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept { // This object will be deleted when the network is destroyed, which will // call Normalize::destroy() diff --git a/plugin/normalizePlugin/normalizePlugin.h b/plugin/normalizePlugin/normalizePlugin.h index 3bf9077f..e7085e1e 100644 --- a/plugin/normalizePlugin/normalizePlugin.h +++ b/plugin/normalizePlugin/normalizePlugin.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_NORMALIZE_PLUGIN_H #define TRT_NORMALIZE_PLUGIN_H #include "cudnn.h" @@ -40,51 +39,51 @@ public: ~Normalize() override = default; - int getNbOutputs() const override; + int getNbOutputs() const noexcept override; - Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) override; + Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept override; - int initialize() override; + int initialize() noexcept override; - void terminate() override; + void terminate() noexcept override; - size_t getWorkspaceSize(int maxBatchSize) const override; + size_t getWorkspaceSize(int maxBatchSize) const noexcept override; - int enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) override; + int enqueue(int batchSize, const void* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept override; - size_t getSerializationSize() const override; + size_t getSerializationSize() const noexcept override; - void serialize(void* buffer) const override; + void serialize(void* buffer) const noexcept override; - bool supportsFormat(DataType type, PluginFormat format) const override; + bool supportsFormat(DataType type, PluginFormat format) const noexcept override; - const char* getPluginType() const override; + const char* getPluginType() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - void destroy() override; + void destroy() noexcept override; - IPluginV2Ext* clone() const override; + IPluginV2Ext* clone() const noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; - DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept override; - bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const override; + bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept override; - bool canBroadcastInputAcrossBatch(int inputIndex) const override; + bool canBroadcastInputAcrossBatch(int inputIndex) const noexcept override; void attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) override; + cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept override; void configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) override; + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept override; - void detachFromContext() override; + void detachFromContext() noexcept override; private: Weights copyToDevice(const void* hostData, size_t count); @@ -111,15 +110,15 @@ public: ~NormalizePluginCreator() override = default; - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const PluginFieldCollection* getFieldNames() override; + const PluginFieldCollection* getFieldNames() noexcept override; - IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) override; + IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; - IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; private: static PluginFieldCollection mFC; diff --git a/plugin/nvFasterRCNN/nvFasterRCNNPlugin.cpp b/plugin/nvFasterRCNN/nvFasterRCNNPlugin.cpp index 2f7d1177..98dd5b0a 100644 --- a/plugin/nvFasterRCNN/nvFasterRCNNPlugin.cpp +++ b/plugin/nvFasterRCNN/nvFasterRCNNPlugin.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "nvFasterRCNNPlugin.h" #include #include @@ -114,38 +113,39 @@ RPROIPlugin::~RPROIPlugin() } } -int RPROIPlugin::initialize() +int RPROIPlugin::initialize() noexcept { return STATUS_SUCCESS; } -int RPROIPlugin::getNbOutputs() const +int RPROIPlugin::getNbOutputs() const noexcept { return 2; } -Dims RPROIPlugin::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) +Dims RPROIPlugin::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept { ASSERT(index >= 0 && index < 2); ASSERT(nbInputDims == 4); ASSERT(inputs[0].nbDims == 3 && inputs[1].nbDims == 3 && inputs[2].nbDims == 3 && inputs[3].nbDims == 3); if (index == 0) // rois { - return DimsCHW(1, params.nmsMaxOut, 4); + return Dims3(1, params.nmsMaxOut, 4); } // Feature map of each ROI after ROI Pooling else // pool5 { - return DimsNCHW(params.nmsMaxOut, inputs[2].d[0], params.poolingH, params.poolingW); + return Dims4(params.nmsMaxOut, inputs[2].d[0], params.poolingH, params.poolingW); } } -size_t RPROIPlugin::getWorkspaceSize(int maxBatchSize) const +size_t RPROIPlugin::getWorkspaceSize(int maxBatchSize) const noexcept { return RPROIInferenceFusedWorkspaceSize(maxBatchSize, A, H, W, params.nmsMaxOut); } -int RPROIPlugin::enqueue(int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) +int RPROIPlugin::enqueue( + int batchSize, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { // Bounding box (region proposal) objectness scores. const void* const scores = inputs[0]; @@ -165,12 +165,11 @@ int RPROIPlugin::enqueue(int batchSize, const void* const* inputs, void** output params.featureStride, params.preNmsTop, params.nmsMaxOut, params.iouThreshold, params.minBoxSize, params.spatialScale, (const float*) iinfo, this->anchorsDev, nvinfer1::DataType::kFLOAT, NCHW, scores, nvinfer1::DataType::kFLOAT, NCHW, deltas, nvinfer1::DataType::kFLOAT, NCHW, fmap, workspace, - nvinfer1::DataType::kFLOAT, rois, nvinfer1::DataType::kFLOAT, NCHW, pfmap); - ASSERT(status == STATUS_SUCCESS); - return 0; + nvinfer1::DataType::kFLOAT, rois, nvinfer1::DataType::kFLOAT, NCHW, pfmap); + return status; } -size_t RPROIPlugin::getSerializationSize() const +size_t RPROIPlugin::getSerializationSize() const noexcept { size_t paramSize = sizeof(RPROIParams); size_t intSize = sizeof(int) * 4; @@ -179,7 +178,7 @@ size_t RPROIPlugin::getSerializationSize() const return paramSize + intSize + ratiosSize + scalesSize; } -void RPROIPlugin::serialize(void* buffer) const +void RPROIPlugin::serialize(void* buffer) const noexcept { char *d = reinterpret_cast(buffer), *a = d; *reinterpret_cast(d) = params; @@ -197,7 +196,7 @@ void RPROIPlugin::serialize(void* buffer) const ASSERT(d == a + getSerializationSize()); } -float* RPROIPlugin::copyToHost(const void* srcHostData, int count) +float* RPROIPlugin::copyToHost(const void* srcHostData, int count) noexcept { float* dstHostPtr = nullptr; CHECK(cudaMallocHost(&dstHostPtr, count * sizeof(float))); @@ -205,35 +204,35 @@ float* RPROIPlugin::copyToHost(const void* srcHostData, int count) return dstHostPtr; } -int RPROIPlugin::copyFromHost(char* dstHostBuffer, const void* source, int count) const +int RPROIPlugin::copyFromHost(char* dstHostBuffer, const void* source, int count) const noexcept { cudaMemcpy(dstHostBuffer, source, count * sizeof(float), cudaMemcpyHostToHost); return count * sizeof(float); } -bool RPROIPlugin::supportsFormat(DataType type, PluginFormat format) const +bool RPROIPlugin::supportsFormat(DataType type, PluginFormat format) const noexcept { - return (type == DataType::kFLOAT && format == PluginFormat::kNCHW); + return (type == DataType::kFLOAT && format == PluginFormat::kLINEAR); } -const char* RPROIPlugin::getPluginType() const +const char* RPROIPlugin::getPluginType() const noexcept { return RPROI_PLUGIN_NAME; } -const char* RPROIPlugin::getPluginVersion() const +const char* RPROIPlugin::getPluginVersion() const noexcept { return RPROI_PLUGIN_VERSION; } -void RPROIPlugin::terminate() {} +void RPROIPlugin::terminate() noexcept {} -void RPROIPlugin::destroy() +void RPROIPlugin::destroy() noexcept { delete this; } -IPluginV2Ext* RPROIPlugin::clone() const +IPluginV2Ext* RPROIPlugin::clone() const noexcept { IPluginV2Ext* plugin = new RPROIPlugin(params, anchorsRatiosHost, anchorsScalesHost, A, C, H, W, anchorsDev); plugin->setPluginNamespace(mPluginNamespace.c_str()); @@ -241,31 +240,31 @@ IPluginV2Ext* RPROIPlugin::clone() const } // Set plugin namespace -void RPROIPlugin::setPluginNamespace(const char* pluginNamespace) +void RPROIPlugin::setPluginNamespace(const char* pluginNamespace) noexcept { mPluginNamespace = pluginNamespace; } -const char* RPROIPlugin::getPluginNamespace() const +const char* RPROIPlugin::getPluginNamespace() const noexcept { return mPluginNamespace.c_str(); } // Return the DataType of the plugin output at the requested index. -DataType RPROIPlugin::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const +DataType RPROIPlugin::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { // Two outputs ASSERT(index == 0 || index == 1); return DataType::kFLOAT; } // Return true if output tensor is broadcast across a batch. -bool RPROIPlugin::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const +bool RPROIPlugin::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept { return false; } // Return true if plugin can use input that is broadcast across batch without replication. -bool RPROIPlugin::canBroadcastInputAcrossBatch(int inputIndex) const +bool RPROIPlugin::canBroadcastInputAcrossBatch(int inputIndex) const noexcept { return false; } @@ -280,9 +279,9 @@ bool RPROIPlugin::canBroadcastInputAcrossBatch(int inputIndex) const // maxbatchSize: maximum batch size for the plugin layer void RPROIPlugin::configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept { - ASSERT(*inputTypes == DataType::kFLOAT && floatFormat == PluginFormat::kNCHW); + ASSERT(*inputTypes == DataType::kFLOAT && floatFormat == PluginFormat::kLINEAR); A = params.anchorsRatioCount * params.anchorsScaleCount; C = inputDims[2].d[0]; @@ -301,12 +300,12 @@ void RPROIPlugin::configurePlugin(const Dims* inputDims, int nbInputs, const Dim } // Attach the plugin object to an execution context and grant the plugin the access to some context resource. -void RPROIPlugin::attachToContext(cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) +void RPROIPlugin::attachToContext(cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept { } // Detach the plugin object from its execution context. -void RPROIPlugin::detachFromContext() {} +void RPROIPlugin::detachFromContext() noexcept {} RPROIPluginCreator::RPROIPluginCreator() { @@ -336,22 +335,22 @@ RPROIPluginCreator::~RPROIPluginCreator() // Free allocated memory (if any) here } -const char* RPROIPluginCreator::getPluginName() const +const char* RPROIPluginCreator::getPluginName() const noexcept { return RPROI_PLUGIN_NAME; } -const char* RPROIPluginCreator::getPluginVersion() const +const char* RPROIPluginCreator::getPluginVersion() const noexcept { return RPROI_PLUGIN_VERSION; } -const PluginFieldCollection* RPROIPluginCreator::getFieldNames() +const PluginFieldCollection* RPROIPluginCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2Ext* RPROIPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2Ext* RPROIPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { const PluginField* fields = fc->fields; int nbFields = fc->nbFields; @@ -440,7 +439,7 @@ IPluginV2Ext* RPROIPluginCreator::createPlugin(const char* name, const PluginFie return plugin; } -IPluginV2Ext* RPROIPluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) +IPluginV2Ext* RPROIPluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept { // This object will be deleted when the network is destroyed, which will // call RPROIPlugin::terminate() diff --git a/plugin/nvFasterRCNN/nvFasterRCNNPlugin.h b/plugin/nvFasterRCNN/nvFasterRCNNPlugin.h index 73767bfb..252ecb04 100644 --- a/plugin/nvFasterRCNN/nvFasterRCNNPlugin.h +++ b/plugin/nvFasterRCNN/nvFasterRCNNPlugin.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_NV_PLUGIN_FASTER_RCNN_H #define TRT_NV_PLUGIN_FASTER_RCNN_H @@ -39,56 +38,56 @@ public: ~RPROIPlugin() override; - int getNbOutputs() const override; + int getNbOutputs() const noexcept override; - Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) override; + Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept override; - int initialize() override; + int initialize() noexcept override; - void terminate() override; + void terminate() noexcept override; - size_t getWorkspaceSize(int maxBatchSize) const override; + size_t getWorkspaceSize(int maxBatchSize) const noexcept override; - int enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) override; + int enqueue(int batchSize, const void* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept override; - size_t getSerializationSize() const override; + size_t getSerializationSize() const noexcept override; - void serialize(void* buffer) const override; + void serialize(void* buffer) const noexcept override; - bool supportsFormat(DataType type, PluginFormat format) const override; + bool supportsFormat(DataType type, PluginFormat format) const noexcept override; - const char* getPluginType() const override; + const char* getPluginType() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - void destroy() override; + void destroy() noexcept override; - IPluginV2Ext* clone() const override; + IPluginV2Ext* clone() const noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; - DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept override; - bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const override; + bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept override; - bool canBroadcastInputAcrossBatch(int inputIndex) const override; + bool canBroadcastInputAcrossBatch(int inputIndex) const noexcept override; void attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) override; + cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept override; void configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) override; + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept override; - void detachFromContext() override; + void detachFromContext() noexcept override; private: - float* copyToHost(const void* srcHostData, int count); + float* copyToHost(const void* srcHostData, int count) noexcept; - int copyFromHost(char* dstHostBuffer, const void* source, int count) const; + int copyFromHost(char* dstHostBuffer, const void* source, int count) const noexcept; // These won't be serialized float* anchorsDev{nullptr}; @@ -107,15 +106,15 @@ public: ~RPROIPluginCreator() override; - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const PluginFieldCollection* getFieldNames() override; + const PluginFieldCollection* getFieldNames() noexcept override; - IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) override; + IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; - IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; private: static PluginFieldCollection mFC; diff --git a/plugin/priorBoxPlugin/priorBoxPlugin.cpp b/plugin/priorBoxPlugin/priorBoxPlugin.cpp index 34031f2a..594ec5d7 100644 --- a/plugin/priorBoxPlugin/priorBoxPlugin.cpp +++ b/plugin/priorBoxPlugin/priorBoxPlugin.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "priorBoxPlugin.h" #include #include @@ -37,15 +36,31 @@ PluginFieldCollection PriorBoxPluginCreator::mFC{}; std::vector PriorBoxPluginCreator::mPluginAttributes; // Constructor -PriorBox::PriorBox(PriorBoxParameters param, int H, int W) +PriorBox::PriorBox(PriorBoxParameters param, int32_t H, int32_t W) : mParam(param) , mH(H) , mW(W) { + // each obj should manage its copy of param + auto copyParamData = [](float*& dest, const float* src, const size_t size) { + if (size > 0) + { + dest = new float[size]; + std::copy_n(src, size, dest); + } + else + { + ASSERT(dest == nullptr); + } + }; + copyParamData(mParam.minSize, param.minSize, param.numMinSize); + copyParamData(mParam.maxSize, param.maxSize, param.numMaxSize); + copyParamData(mParam.aspectRatios, param.aspectRatios, param.numAspectRatios); + setupDeviceMemory(); } -void PriorBox::setupDeviceMemory() +void PriorBox::setupDeviceMemory() noexcept { auto copyToDevice = [](const void* hostData, size_t count) -> Weights { void* deviceData = nullptr; @@ -56,7 +71,7 @@ void PriorBox::setupDeviceMemory() // minSize is required and needs to be non-negative ASSERT(mParam.numMinSize > 0 && mParam.minSize != nullptr); - for (int i = 0; i < mParam.numMinSize; ++i) + for (auto i = 0; i < mParam.numMinSize; ++i) { ASSERT(mParam.minSize[i] > 0 && "minSize must be positive"); } @@ -65,7 +80,7 @@ void PriorBox::setupDeviceMemory() ASSERT(mParam.numAspectRatios >= 0 && mParam.aspectRatios != nullptr); // Aspect ratio of 1.0 is built in. std::vector tmpAR(1, 1); - for (int i = 0; i < mParam.numAspectRatios; ++i) + for (auto i = 0; i < mParam.numAspectRatios; ++i) { float ar = mParam.aspectRatios[i]; bool alreadyExist = false; @@ -107,7 +122,7 @@ void PriorBox::setupDeviceMemory() if (mParam.numMaxSize > 0) { ASSERT(mParam.numMinSize == mParam.numMaxSize && mParam.maxSize != nullptr); - for (int i = 0; i < mParam.numMaxSize; ++i) + for (auto i = 0; i < mParam.numMaxSize; ++i) { // maxSize should be greater than minSize ASSERT(mParam.maxSize[i] > mParam.minSize[i] && "maxSize must be greater than minSize"); @@ -122,11 +137,11 @@ PriorBox::PriorBox(const void* data, size_t length) const char *d = static_cast(data), *a = d; mParam = read(d); - auto readArray = [&d](const int size, float*& array) { + auto readArray = [&d](const int32_t size, float*& array) { if (size > 0) { array = new float[size]; - for (int i = 0; i < size; i++) + for (auto i = 0; i < size; i++) { array[i] = read(d); } @@ -149,14 +164,14 @@ PriorBox::PriorBox(const void* data, size_t length) } // Returns the number of output from the plugin layer -int PriorBox::getNbOutputs() const +int32_t PriorBox::getNbOutputs() const noexcept { // Number of outputs from the plugin layer is 1 return 1; } // Computes and returns the output dimensions -Dims PriorBox::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) +Dims PriorBox::getOutputDimensions(int32_t index, const Dims* inputs, int32_t nbInputDims) noexcept { ASSERT(nbInputDims == 2); // Only one output from the plugin layer @@ -166,45 +181,44 @@ Dims PriorBox::getOutputDimensions(int index, const Dims* inputs, int nbInputDim // workaround for TRT // The first channel is for prior box coordinates. // The second channel is for prior box scaling factors, which is simply a copy of the variance provided. - return DimsCHW(2, mH * mW * mNumPriors * 4, 1); + return Dims3(2, mH * mW * mNumPriors * 4, 1); } -int PriorBox::initialize() +int32_t PriorBox::initialize() noexcept { return STATUS_SUCCESS; } -size_t PriorBox::getWorkspaceSize(int /*maxBatchSize*/) const +size_t PriorBox::getWorkspaceSize(int32_t /*maxBatchSize*/) const noexcept { return 0; } -int PriorBox::enqueue( - int /*batchSize*/, const void* const* /*inputs*/, void** outputs, void* /*workspace*/, cudaStream_t stream) +int32_t PriorBox::enqueue(int32_t /*batchSize*/, const void* const* /*inputs*/, void* const* outputs, void* /*workspace*/, + cudaStream_t stream) noexcept { void* outputData = outputs[0]; pluginStatus_t status = priorBoxInference(stream, mParam, mH, mW, mNumPriors, aspectRatios.count, minSize.values, maxSize.values, aspectRatios.values, outputData); - ASSERT(status == STATUS_SUCCESS); - return 0; + return status; } // Returns the size of serialized parameters -size_t PriorBox::getSerializationSize() const +size_t PriorBox::getSerializationSize() const noexcept { // PriorBoxParameters, minSize, maxSize, aspectRatios, mH, mW - the construct parameters return sizeof(PriorBoxParameters) + sizeof(float) * (mParam.numMinSize + mParam.numMaxSize + mParam.numAspectRatios) + sizeof(int) * 2; } -void PriorBox::serialize(void* buffer) const +void PriorBox::serialize(void* buffer) const noexcept { char *d = static_cast(buffer), *a = d; write(d, mParam); - auto writeArray = [&d](const int size, const float* array) { - for (int i = 0; i < size; i++) + auto writeArray = [&d](const int32_t size, const float* array) { + for (auto i = 0; i < size; i++) { write(d, array[i]); } @@ -219,22 +233,22 @@ void PriorBox::serialize(void* buffer) const ASSERT(d == a + getSerializationSize()); } -bool PriorBox::supportsFormat(DataType type, PluginFormat format) const +bool PriorBox::supportsFormat(DataType type, PluginFormat format) const noexcept { - return (type == DataType::kFLOAT && format == PluginFormat::kNCHW); + return (type == DataType::kFLOAT && format == PluginFormat::kLINEAR); } -const char* PriorBox::getPluginType() const +const char* PriorBox::getPluginType() const noexcept { return PRIOR_BOX_PLUGIN_NAME; } -const char* PriorBox::getPluginVersion() const +const char* PriorBox::getPluginVersion() const noexcept { return PRIOR_BOX_PLUGIN_VERSION; } -void PriorBox::destroy() +void PriorBox::destroy() noexcept { CUASSERT(cudaFree(const_cast(minSize.values))); if (mParam.numMaxSize > 0) @@ -252,43 +266,26 @@ void PriorBox::destroy() delete this; } -IPluginV2Ext* PriorBox::clone() const +IPluginV2Ext* PriorBox::clone() const noexcept { - // each obj should manage its copy of param - PriorBoxParameters params = mParam; - auto copyParamData = [](float*& dest, const float* src, const size_t size) { - if (size > 0) - { - dest = new float[size]; - std::copy_n(src, size, dest); - } - else - { - ASSERT(dest == nullptr); - } - }; - copyParamData(params.minSize, mParam.minSize, mParam.numMinSize); - copyParamData(params.maxSize, mParam.maxSize, mParam.numMaxSize); - copyParamData(params.aspectRatios, mParam.aspectRatios, mParam.numAspectRatios); - - PriorBox* obj = new PriorBox(params, mH, mW); + PriorBox* obj = new PriorBox(mParam, mH, mW); obj->setPluginNamespace(mPluginNamespace.c_str()); return obj; } // Set plugin namespace -void PriorBox::setPluginNamespace(const char* pluginNamespace) +void PriorBox::setPluginNamespace(const char* pluginNamespace) noexcept { mPluginNamespace = pluginNamespace; } -const char* PriorBox::getPluginNamespace() const +const char* PriorBox::getPluginNamespace() const noexcept { return mPluginNamespace.c_str(); } // Return the DataType of the plugin output at the requested index. -DataType PriorBox::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const +DataType PriorBox::getOutputDataType(int32_t index, const nvinfer1::DataType* /*inputTypes*/, int32_t /*nbInputs*/) const noexcept { // Two outputs ASSERT(index == 0 || index == 1); @@ -296,23 +293,23 @@ DataType PriorBox::getOutputDataType(int index, const nvinfer1::DataType* inputT } // Return true if output tensor is broadcast across a batch. -bool PriorBox::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const +bool PriorBox::isOutputBroadcastAcrossBatch(int32_t /*outputIndex*/, const bool* /*inputIsBroadcasted*/, int32_t /*nbInputs*/) const noexcept { return false; } // Return true if plugin can use input that is broadcast across batch without replication. -bool PriorBox::canBroadcastInputAcrossBatch(int inputIndex) const +bool PriorBox::canBroadcastInputAcrossBatch(int32_t /*inputIndex*/) const noexcept { return false; } // Configure the layer with input and output data types. -void PriorBox::configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, - const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) +void PriorBox::configurePlugin(const Dims* inputDims, int32_t nbInputs, const Dims* outputDims, int32_t nbOutputs, + const DataType* inputTypes, const DataType* /*outputTypes*/, const bool* /*inputIsBroadcast*/, + const bool* /*outputIsBroadcast*/, PluginFormat floatFormat, int32_t /*maxBatchSize*/) noexcept { - ASSERT(*inputTypes == DataType::kFLOAT && floatFormat == PluginFormat::kNCHW); + ASSERT(*inputTypes == DataType::kFLOAT && floatFormat == PluginFormat::kLINEAR); ASSERT(nbInputs == 2); ASSERT(nbOutputs == 1); ASSERT(inputDims[0].nbDims == 3); @@ -334,10 +331,10 @@ void PriorBox::configurePlugin(const Dims* inputDims, int nbInputs, const Dims* } // Attach the plugin object to an execution context and grant the plugin the access to some context resource. -void PriorBox::attachToContext(cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) {} +void PriorBox::attachToContext(cudnnContext* /*cudnnContext*/, cublasContext* /*cublasContext*/, IGpuAllocator* /*gpuAllocator*/) noexcept {} // Detach the plugin object from its execution context. -void PriorBox::detachFromContext() {} +void PriorBox::detachFromContext() noexcept {} PriorBoxPluginCreator::PriorBoxPluginCreator() { @@ -362,82 +359,101 @@ PriorBoxPluginCreator::~PriorBoxPluginCreator() // Free allocated memory (if any) here } -const char* PriorBoxPluginCreator::getPluginName() const +const char* PriorBoxPluginCreator::getPluginName() const noexcept { return PRIOR_BOX_PLUGIN_NAME; } -const char* PriorBoxPluginCreator::getPluginVersion() const +const char* PriorBoxPluginCreator::getPluginVersion() const noexcept { return PRIOR_BOX_PLUGIN_VERSION; } -const PluginFieldCollection* PriorBoxPluginCreator::getFieldNames() +const PluginFieldCollection* PriorBoxPluginCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2Ext* PriorBoxPluginCreator::createPlugin(const char* /*name*/, const PluginFieldCollection* fc) +IPluginV2Ext* PriorBoxPluginCreator::createPlugin(const char* /*name*/, const PluginFieldCollection* fc) noexcept { const PluginField* fields = fc->fields; PriorBoxParameters params; - for (int i = 0; i < fc->nbFields; ++i) + std::unique_ptr minSize; + std::unique_ptr maxSize; + std::unique_ptr aspectRatios; + for (auto i = 0; i < fc->nbFields; ++i) { const char* attrName = fields[i].name; if (!strcmp(attrName, "minSize")) { ASSERT(fields[i].type == PluginFieldType::kFLOAT32); - int size = fields[i].length; - params.minSize = new float[size]; - const auto* minS = static_cast(fields[i].data); - for (int j = 0; j < size; j++) - { - params.minSize[j] = *minS; - minS++; - } + const int32_t size = fields[i].length; params.numMinSize = size; + if (size > 0) + { + minSize.reset(new float[size]); + params.minSize = minSize.get(); + const auto* minS = static_cast(fields[i].data); + for (auto j = 0; j < size; j++) + { + params.minSize[j] = *minS; + minS++; + } + } + else + { + params.minSize = nullptr; + } } else if (!strcmp(attrName, "maxSize")) { ASSERT(fields[i].type == PluginFieldType::kFLOAT32); - int size = fields[i].length; + const int32_t size = fields[i].length; params.numMaxSize = size; - params.maxSize = nullptr; if (size > 0) { - params.maxSize = new float[size]; + maxSize.reset(new float[size]); + params.maxSize = maxSize.get(); const auto* maxS = static_cast(fields[i].data); - for (int j = 0; j < size; j++) + for (auto j = 0; j < size; j++) { params.maxSize[j] = *maxS; maxS++; } } + else + { + params.maxSize = nullptr; + } } else if (!strcmp(attrName, "aspectRatios")) { ASSERT(fields[i].type == PluginFieldType::kFLOAT32); - int size = fields[i].length; + const int32_t size = fields[i].length; params.numAspectRatios = size; - params.aspectRatios = nullptr; if (size > 0) { - params.aspectRatios = new float[size]; + aspectRatios.reset(new float[size]); + params.aspectRatios = aspectRatios.get(); const auto* aR = static_cast(fields[i].data); - for (int j = 0; j < size; j++) + for (auto j = 0; j < size; j++) { params.aspectRatios[j] = *aR; aR++; } } + else + { + params.aspectRatios = nullptr; + } } else if (!strcmp(attrName, "variance")) { ASSERT(fields[i].type == PluginFieldType::kFLOAT32); - int size = fields[i].length; + const int32_t size = fields[i].length; const auto* lVar = static_cast(fields[i].data); - for (int j = 0; j < size; j++) + for (auto j = 0; j < size; j++) { params.variance[j] = (*lVar); lVar++; @@ -485,7 +501,7 @@ IPluginV2Ext* PriorBoxPluginCreator::createPlugin(const char* /*name*/, const Pl } IPluginV2Ext* PriorBoxPluginCreator::deserializePlugin( - const char* /*name*/, const void* serialData, size_t serialLength) + const char* /*name*/, const void* serialData, size_t serialLength) noexcept { // This object will be deleted when the network is destroyed, which will // call PriorBox::destroy() diff --git a/plugin/priorBoxPlugin/priorBoxPlugin.h b/plugin/priorBoxPlugin/priorBoxPlugin.h index 139ef3a7..f5d95b08 100644 --- a/plugin/priorBoxPlugin/priorBoxPlugin.h +++ b/plugin/priorBoxPlugin/priorBoxPlugin.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_PRIOR_BOX_PLUGIN_H #define TRT_PRIOR_BOX_PLUGIN_H #include "cudnn.h" @@ -32,60 +31,60 @@ namespace plugin class PriorBox : public IPluginV2Ext { public: - PriorBox(PriorBoxParameters param, int H = 0, int W = 0); + PriorBox(PriorBoxParameters param, int32_t H = 0, int32_t W = 0); PriorBox(const void* buffer, size_t length); ~PriorBox() override = default; - int getNbOutputs() const override; + int32_t getNbOutputs() const noexcept override; - Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) override; + Dims getOutputDimensions(int32_t index, const Dims* inputs, int32_t nbInputDims) noexcept override; - int initialize() override; + int32_t initialize() noexcept override; - void terminate() override{}; + void terminate() noexcept override {}; - size_t getWorkspaceSize(int maxBatchSize) const override; + size_t getWorkspaceSize(int32_t maxBatchSize) const noexcept override; - int enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) override; + int32_t enqueue(int32_t batchSize, const void* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept override; - size_t getSerializationSize() const override; + size_t getSerializationSize() const noexcept override; - void serialize(void* buffer) const override; + void serialize(void* buffer) const noexcept override; - bool supportsFormat(DataType type, PluginFormat format) const override; + bool supportsFormat(DataType type, PluginFormat format) const noexcept override; - const char* getPluginType() const override; + const char* getPluginType() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - void destroy() override; + void destroy() noexcept override; - IPluginV2Ext* clone() const override; + IPluginV2Ext* clone() const noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; - DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + DataType getOutputDataType(int32_t index, const nvinfer1::DataType* inputTypes, int32_t nbInputs) const noexcept override; - bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const override; + bool isOutputBroadcastAcrossBatch(int32_t outputIndex, const bool* inputIsBroadcasted, int32_t nbInputs) const noexcept override; - bool canBroadcastInputAcrossBatch(int inputIndex) const override; + bool canBroadcastInputAcrossBatch(int32_t inputIndex) const noexcept override; void attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) override; + cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept override; - void configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, + void configurePlugin(const Dims* inputDims, int32_t nbInputs, const Dims* outputDims, int32_t nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) override; + const bool* outputIsBroadcast, PluginFormat floatFormat, int32_t maxBatchSize) noexcept override; - void detachFromContext() override; + void detachFromContext() noexcept override; private: - void setupDeviceMemory(); + void setupDeviceMemory() noexcept; PriorBoxParameters mParam; int32_t mNumPriors; @@ -104,15 +103,15 @@ public: ~PriorBoxPluginCreator() override; - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const PluginFieldCollection* getFieldNames() override; + const PluginFieldCollection* getFieldNames() noexcept override; - IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) override; + IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; - IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; private: static PluginFieldCollection mFC; diff --git a/plugin/proposalLayerPlugin/proposalLayerPlugin.cpp b/plugin/proposalLayerPlugin/proposalLayerPlugin.cpp index 82b1b706..a1fb202a 100644 --- a/plugin/proposalLayerPlugin/proposalLayerPlugin.cpp +++ b/plugin/proposalLayerPlugin/proposalLayerPlugin.cpp @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "proposalLayerPlugin.h" #include "mrcnn_config.h" #include "plugin.h" -#include #include +#include #include #include @@ -48,22 +47,22 @@ ProposalLayerPluginCreator::ProposalLayerPluginCreator() mFC.fields = mPluginAttributes.data(); } -const char* ProposalLayerPluginCreator::getPluginName() const +const char* ProposalLayerPluginCreator::getPluginName() const noexcept { return PROPOSALLAYER_PLUGIN_NAME; -}; +} -const char* ProposalLayerPluginCreator::getPluginVersion() const +const char* ProposalLayerPluginCreator::getPluginVersion() const noexcept { return PROPOSALLAYER_PLUGIN_VERSION; -}; +} -const PluginFieldCollection* ProposalLayerPluginCreator::getFieldNames() +const PluginFieldCollection* ProposalLayerPluginCreator::getFieldNames() noexcept { return &mFC; -}; +} -IPluginV2Ext* ProposalLayerPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2Ext* ProposalLayerPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { auto image_size = MaskRCNNConfig::IMAGE_SHAPE; const PluginField* fields = fc->fields; @@ -88,17 +87,17 @@ IPluginV2Ext* ProposalLayerPluginCreator::createPlugin(const char* name, const P if (!strcmp(attrName, "image_size")) { assert(fields[i].type == PluginFieldType::kINT32); - const auto dims = static_cast(fields[i].data); + const auto* const dims = static_cast(fields[i].data); std::copy_n(dims, 3, image_size.d); } } return new ProposalLayer(mPreNMSTopK, mKeepTopK, mIOUThreshold, image_size); -}; +} -IPluginV2Ext* ProposalLayerPluginCreator::deserializePlugin(const char* name, const void* data, size_t length) +IPluginV2Ext* ProposalLayerPluginCreator::deserializePlugin(const char* name, const void* data, size_t length) noexcept { return new ProposalLayer(data, length); -}; +} ProposalLayer::ProposalLayer(int prenms_topk, int keep_topk, float iou_threshold, const nvinfer1::Dims& image_size) : mPreNMSTopK(prenms_topk) @@ -109,7 +108,7 @@ ProposalLayer::ProposalLayer(int prenms_topk, int keep_topk, float iou_threshold mBackgroundLabel = -1; assert(mPreNMSTopK > 0); assert(mKeepTopK > 0); - assert(iou_threshold > 0.0f); + assert(iou_threshold > 0.0F); mParam.backgroundLabelId = -1; mParam.numClasses = 1; @@ -120,14 +119,14 @@ ProposalLayer::ProposalLayer(int prenms_topk, int keep_topk, float iou_threshold mType = DataType::kFLOAT; generate_pyramid_anchors(image_size); -}; +} -int ProposalLayer::getNbOutputs() const +int ProposalLayer::getNbOutputs() const noexcept { return 1; -}; +} -int ProposalLayer::initialize() +int ProposalLayer::initialize() noexcept { // Init the mValidCnt of max batch size std::vector tempValidCnt(mMaxBatchSize, mPreNMSTopK); @@ -148,53 +147,53 @@ int ProposalLayer::initialize() } return 0; -}; +} -void ProposalLayer::terminate(){}; +void ProposalLayer::terminate() noexcept {} -void ProposalLayer::destroy() +void ProposalLayer::destroy() noexcept { delete this; -}; +} -bool ProposalLayer::supportsFormat(DataType type, PluginFormat format) const +bool ProposalLayer::supportsFormat(DataType type, PluginFormat format) const noexcept { - return (type == DataType::kFLOAT && format == PluginFormat::kNCHW); -}; + return (type == DataType::kFLOAT && format == PluginFormat::kLINEAR); +} -const char* ProposalLayer::getPluginType() const +const char* ProposalLayer::getPluginType() const noexcept { return PROPOSALLAYER_PLUGIN_NAME; -}; +} -const char* ProposalLayer::getPluginVersion() const +const char* ProposalLayer::getPluginVersion() const noexcept { return PROPOSALLAYER_PLUGIN_VERSION; -}; +} -IPluginV2Ext* ProposalLayer::clone() const +IPluginV2Ext* ProposalLayer::clone() const noexcept { - auto plugin = new ProposalLayer(*this); + auto* plugin = new ProposalLayer(*this); plugin->setPluginNamespace(mNameSpace.c_str()); return plugin; -}; +} -void ProposalLayer::setPluginNamespace(const char* libNamespace) +void ProposalLayer::setPluginNamespace(const char* libNamespace) noexcept { mNameSpace = libNamespace; -}; +} -const char* ProposalLayer::getPluginNamespace() const +const char* ProposalLayer::getPluginNamespace() const noexcept { return mNameSpace.c_str(); -}; +} -size_t ProposalLayer::getSerializationSize() const +size_t ProposalLayer::getSerializationSize() const noexcept { return sizeof(int) * 2 + sizeof(float) + sizeof(int) * 2 + sizeof(nvinfer1::Dims); -}; +} -void ProposalLayer::serialize(void* buffer) const +void ProposalLayer::serialize(void* buffer) const noexcept { char *d = reinterpret_cast(buffer), *a = d; write(d, mPreNMSTopK); @@ -204,7 +203,7 @@ void ProposalLayer::serialize(void* buffer) const write(d, mAnchorsCnt); write(d, mImageSize); ASSERT(d == a + getSerializationSize()); -}; +} ProposalLayer::ProposalLayer(const void* data, size_t length) { @@ -231,7 +230,7 @@ ProposalLayer::ProposalLayer(const void* data, size_t length) mType = DataType::kFLOAT; generate_pyramid_anchors(mImageSize); -}; +} void ProposalLayer::check_valid_inputs(const nvinfer1::Dims* inputs, int nbInputDims) { @@ -243,16 +242,16 @@ void ProposalLayer::check_valid_inputs(const nvinfer1::Dims* inputs, int nbInput assert(inputs[0].nbDims == 3 && inputs[0].d[1] == 2); // foreground_delta assert(inputs[1].nbDims == 3 && inputs[1].d[1] == 4); -}; +} -size_t ProposalLayer::getWorkspaceSize(int batch_size) const +size_t ProposalLayer::getWorkspaceSize(int batch_size) const noexcept { ProposalWorkSpace proposal(batch_size, mAnchorsCnt, mPreNMSTopK, mParam, mType); return proposal.totalSize; -}; +} -Dims ProposalLayer::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) +Dims ProposalLayer::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept { check_valid_inputs(inputs, nbInputDims); @@ -269,9 +268,10 @@ Dims ProposalLayer::getOutputDimensions(int index, const Dims* inputs, int nbInp return proposals; } -void ProposalLayer::generate_pyramid_anchors(const nvinfer1::Dims& image_dims) +void ProposalLayer::generate_pyramid_anchors(const nvinfer1::Dims& image_dims) noexcept { assert(image_dims.nbDims == 3 && image_dims.d[0] == 3); + const auto& scales = MaskRCNNConfig::RPN_ANCHOR_SCALES; const auto& ratios = MaskRCNNConfig::RPN_ANCHOR_RATIOS; const auto& strides = MaskRCNNConfig::BACKBONE_STRIDES; @@ -281,7 +281,7 @@ void ProposalLayer::generate_pyramid_anchors(const nvinfer1::Dims& image_dims) const float cx = image_dims.d[2] - 1; auto& anchors = mAnchorBoxesHost; - assert(anchors.size() == 0); + assert(anchors.empty()); assert(scales.size() == strides.size()); for (size_t s = 0; s < scales.size(); ++s) @@ -306,7 +306,7 @@ void ProposalLayer::generate_pyramid_anchors(const nvinfer1::Dims& image_dims) } int ProposalLayer::enqueue( - int batch_size, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) + int batch_size, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { void* proposals = outputs[0]; @@ -324,23 +324,23 @@ int ProposalLayer::enqueue( assert(status == cudaSuccess); return status; -}; +} // Return the DataType of the plugin output at the requested index -DataType ProposalLayer::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const +DataType ProposalLayer::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { // Only DataType::kFLOAT is acceptable by the plugin layer return DataType::kFLOAT; } // Return true if output tensor is broadcast across a batch. -bool ProposalLayer::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const +bool ProposalLayer::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept { return false; } // Return true if plugin can use input that is broadcast across batch without replication. -bool ProposalLayer::canBroadcastInputAcrossBatch(int inputIndex) const +bool ProposalLayer::canBroadcastInputAcrossBatch(int inputIndex) const noexcept { return false; } @@ -348,7 +348,7 @@ bool ProposalLayer::canBroadcastInputAcrossBatch(int inputIndex) const // Configure the layer with input and output data types. void ProposalLayer::configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept { check_valid_inputs(inputDims, nbInputs); assert(inputDims[0].d[0] == inputDims[1].d[0]); @@ -360,9 +360,9 @@ void ProposalLayer::configurePlugin(const Dims* inputDims, int nbInputs, const D // Attach the plugin object to an execution context and grant the plugin the access to some context resource. void ProposalLayer::attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) + cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept { } // Detach the plugin object from its execution context. -void ProposalLayer::detachFromContext() {} +void ProposalLayer::detachFromContext() noexcept {} diff --git a/plugin/proposalLayerPlugin/proposalLayerPlugin.h b/plugin/proposalLayerPlugin/proposalLayerPlugin.h index 4b48d835..0e0b4a70 100644 --- a/plugin/proposalLayerPlugin/proposalLayerPlugin.h +++ b/plugin/proposalLayerPlugin/proposalLayerPlugin.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_PROPOSAL_LAYER_PLUGIN_H #define TRT_PROPOSAL_LAYER_PLUGIN_H #include @@ -41,58 +40,58 @@ public: ~ProposalLayer() override = default; - int getNbOutputs() const override; + int getNbOutputs() const noexcept override; - Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) override; + Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept override; - int initialize() override; + int initialize() noexcept override; - void terminate() override; + void terminate() noexcept override; - void destroy() override; + void destroy() noexcept override; - size_t getWorkspaceSize(int maxBatchSize) const override; + size_t getWorkspaceSize(int maxBatchSize) const noexcept override; - int enqueue( - int batch_size, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) override; + int enqueue(int batch_size, const void* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept override; - size_t getSerializationSize() const override; + size_t getSerializationSize() const noexcept override; - void serialize(void* buffer) const override; + void serialize(void* buffer) const noexcept override; // void configureWithFormat(const Dims* inputs, int nbInputs, const Dims* outputDims, int nbOutputs, // nvinfer1::DataType type, nvinfer1::PluginFormat format, int maxBatchSize) override; - bool supportsFormat(DataType type, PluginFormat format) const override; + bool supportsFormat(DataType type, PluginFormat format) const noexcept override; - const char* getPluginType() const override; + const char* getPluginType() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - IPluginV2Ext* clone() const override; + IPluginV2Ext* clone() const noexcept override; - void setPluginNamespace(const char* libNamespace) override; + void setPluginNamespace(const char* libNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; - DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept override; - bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const override; + bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept override; - bool canBroadcastInputAcrossBatch(int inputIndex) const override; + bool canBroadcastInputAcrossBatch(int inputIndex) const noexcept override; void attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) override; + cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept override; void configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) override; + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept override; - void detachFromContext() override; + void detachFromContext() noexcept override; private: void check_valid_inputs(const nvinfer1::Dims* inputs, int nbInputDims); - void generate_pyramid_anchors(const nvinfer1::Dims& image_size); + void generate_pyramid_anchors(const nvinfer1::Dims& image_size) noexcept; int mBackgroundLabel; int mPreNMSTopK; @@ -106,8 +105,8 @@ private: mAnchorBoxesDevice; // [N, anchors(261888 for resnet101 + 1024*1024), (y1, x1, y2, x2)] std::vector mAnchorBoxesHost; - nvinfer1::Dims mImageSize; nvinfer1::DataType mType; + nvinfer1::Dims mImageSize; RefineNMSParameters mParam; std::string mNameSpace; @@ -120,15 +119,15 @@ public: ~ProposalLayerPluginCreator(){}; - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const PluginFieldCollection* getFieldNames() override; + const PluginFieldCollection* getFieldNames() noexcept override; - IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) override; + IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; - IPluginV2Ext* deserializePlugin(const char* name, const void* data, size_t length) override; + IPluginV2Ext* deserializePlugin(const char* name, const void* data, size_t length) noexcept override; private: static PluginFieldCollection mFC; diff --git a/plugin/proposalPlugin/proposalPlugin.cpp b/plugin/proposalPlugin/proposalPlugin.cpp index b1b7b047..781ec74e 100644 --- a/plugin/proposalPlugin/proposalPlugin.cpp +++ b/plugin/proposalPlugin/proposalPlugin.cpp @@ -267,7 +267,7 @@ Dims ProposalPlugin::getOutputDimensions(int index, const Dims* inputs, int nbIn int channels = mMaxBoxNum; int height = 4; int width = 1; - return DimsCHW(channels, height, width); + return Dims3(channels, height, width); } DimsExprs ProposalDynamicPlugin::getOutputDimensions( @@ -310,7 +310,7 @@ size_t ProposalDynamicPlugin::getWorkspaceSize( } int ProposalPlugin::enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) noexcept + int batchSize, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { int status = -1; // Our plugin outputs only one tensor @@ -409,7 +409,7 @@ void ProposalDynamicPlugin::serialize(void* buffer) const noexcept bool ProposalPlugin::supportsFormat(DataType type, PluginFormat format) const noexcept { // This plugin only supports ordinary floats, and NCHW input format - if (type == DataType::kFLOAT && format == PluginFormat::kNCHW) + if (type == DataType::kFLOAT && format == PluginFormat::kLINEAR) { return true; } diff --git a/plugin/proposalPlugin/proposalPlugin.h b/plugin/proposalPlugin/proposalPlugin.h index 40e2b5fa..223e2a33 100644 --- a/plugin/proposalPlugin/proposalPlugin.h +++ b/plugin/proposalPlugin/proposalPlugin.h @@ -61,7 +61,7 @@ public: size_t getWorkspaceSize(int) const noexcept override; - int enqueue(int batchSize, const void* const* inputs, void** outputs, void* workspace, + int enqueue(int batchSize, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; size_t getSerializationSize() const noexcept override; diff --git a/plugin/pyramidROIAlignPlugin/pyramidROIAlignPlugin.cpp b/plugin/pyramidROIAlignPlugin/pyramidROIAlignPlugin.cpp index edbe1b61..be18f4c7 100644 --- a/plugin/pyramidROIAlignPlugin/pyramidROIAlignPlugin.cpp +++ b/plugin/pyramidROIAlignPlugin/pyramidROIAlignPlugin.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "pyramidROIAlignPlugin.h" #include "plugin.h" #include @@ -40,22 +39,22 @@ PyramidROIAlignPluginCreator::PyramidROIAlignPluginCreator() mFC.fields = mPluginAttributes.data(); } -const char* PyramidROIAlignPluginCreator::getPluginName() const +const char* PyramidROIAlignPluginCreator::getPluginName() const noexcept { return PYRAMIDROIALGIN_PLUGIN_NAME; -}; +} -const char* PyramidROIAlignPluginCreator::getPluginVersion() const +const char* PyramidROIAlignPluginCreator::getPluginVersion() const noexcept { return PYRAMIDROIALGIN_PLUGIN_VERSION; -}; +} -const PluginFieldCollection* PyramidROIAlignPluginCreator::getFieldNames() +const PluginFieldCollection* PyramidROIAlignPluginCreator::getFieldNames() noexcept { return &mFC; -}; +} -IPluginV2Ext* PyramidROIAlignPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2Ext* PyramidROIAlignPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { const PluginField* fields = fc->fields; for (int i = 0; i < fc->nbFields; ++i) @@ -68,12 +67,13 @@ IPluginV2Ext* PyramidROIAlignPluginCreator::createPlugin(const char* name, const } } return new PyramidROIAlign(mPooledSize); -}; +} -IPluginV2Ext* PyramidROIAlignPluginCreator::deserializePlugin(const char* name, const void* data, size_t length) +IPluginV2Ext* PyramidROIAlignPluginCreator::deserializePlugin( + const char* name, const void* data, size_t length) noexcept { return new PyramidROIAlign(data, length); -}; +} PyramidROIAlign::PyramidROIAlign(int pooled_size) : mPooledSize({pooled_size, pooled_size}) @@ -83,60 +83,58 @@ PyramidROIAlign::PyramidROIAlign(int pooled_size) // shape mInputSize = MaskRCNNConfig::IMAGE_SHAPE.d[1]; mThresh = (224 * 224 * 2.0f / (mInputSize * mInputSize)) / (4.0 * 4.0f); -}; +} -int PyramidROIAlign::getNbOutputs() const +int PyramidROIAlign::getNbOutputs() const noexcept { return 1; -}; +} -int PyramidROIAlign::initialize() -{ - return 0; -}; - -void PyramidROIAlign::terminate(){ - -}; - -void PyramidROIAlign::destroy() -{ - delete this; -}; - -size_t PyramidROIAlign::getWorkspaceSize(int) const +int PyramidROIAlign::initialize() noexcept { return 0; } -bool PyramidROIAlign::supportsFormat(DataType type, PluginFormat format) const -{ - return (type == DataType::kFLOAT && format == PluginFormat::kNCHW); -}; +void PyramidROIAlign::terminate() noexcept {} -const char* PyramidROIAlign::getPluginType() const +void PyramidROIAlign::destroy() noexcept +{ + delete this; +} + +size_t PyramidROIAlign::getWorkspaceSize(int) const noexcept +{ + return 0; +} + +bool PyramidROIAlign::supportsFormat(DataType type, PluginFormat format) const noexcept +{ + return (type == DataType::kFLOAT && format == PluginFormat::kLINEAR); +} + +const char* PyramidROIAlign::getPluginType() const noexcept { return "PyramidROIAlign_TRT"; -}; +} -const char* PyramidROIAlign::getPluginVersion() const +const char* PyramidROIAlign::getPluginVersion() const noexcept { return "1"; -}; +} -IPluginV2Ext* PyramidROIAlign::clone() const +IPluginV2Ext* PyramidROIAlign::clone() const noexcept { auto plugin = new PyramidROIAlign(*this); plugin->setPluginNamespace(mNameSpace.c_str()); return plugin; -}; +} -void PyramidROIAlign::setPluginNamespace(const char* libNamespace) +void PyramidROIAlign::setPluginNamespace(const char* libNamespace) noexcept { mNameSpace = libNamespace; -}; +} -const char* PyramidROIAlign::getPluginNamespace() const +const char* PyramidROIAlign::getPluginNamespace() const noexcept { return mNameSpace.c_str(); } @@ -161,7 +159,7 @@ void PyramidROIAlign::check_valid_inputs(const nvinfer1::Dims* inputs, int nbInp } } -Dims PyramidROIAlign::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) +Dims PyramidROIAlign::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept { check_valid_inputs(inputs, nbInputDims); @@ -180,10 +178,10 @@ Dims PyramidROIAlign::getOutputDimensions(int index, const Dims* inputs, int nbI result.d[3] = mPooledSize.x; return result; -}; +} int PyramidROIAlign::enqueue( - int batch_size, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) + int batch_size, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { void* pooled = outputs[0]; @@ -194,16 +192,15 @@ int PyramidROIAlign::enqueue( pooled, mPooledSize); - assert(status == cudaSuccess); - return 0; -}; + return status; +} -size_t PyramidROIAlign::getSerializationSize() const +size_t PyramidROIAlign::getSerializationSize() const noexcept { return sizeof(int) * 2 + sizeof(int) * 3 + sizeof(float) + sizeof(int) * 2 * 4; -}; +} -void PyramidROIAlign::serialize(void* buffer) const +void PyramidROIAlign::serialize(void* buffer) const noexcept { char *d = reinterpret_cast(buffer), *a = d; write(d, mPooledSize.y); @@ -221,7 +218,7 @@ void PyramidROIAlign::serialize(void* buffer) const write(d, mFeatureSpatialSize[3].y); write(d, mFeatureSpatialSize[3].x); assert(d == a + getSerializationSize()); -}; +} PyramidROIAlign::PyramidROIAlign(const void* data, size_t length) { @@ -241,23 +238,24 @@ PyramidROIAlign::PyramidROIAlign(const void* data, size_t length) mFeatureSpatialSize[3].x = read(d); assert(d == a + length); -}; +} // Return the DataType of the plugin output at the requested index DataType PyramidROIAlign::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const + noexcept { // Only DataType::kFLOAT is acceptable by the plugin layer return DataType::kFLOAT; } // Return true if output tensor is broadcast across a batch. -bool PyramidROIAlign::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const +bool PyramidROIAlign::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept { return false; } // Return true if plugin can use input that is broadcast across batch without replication. -bool PyramidROIAlign::canBroadcastInputAcrossBatch(int inputIndex) const +bool PyramidROIAlign::canBroadcastInputAcrossBatch(int inputIndex) const noexcept { return false; } @@ -265,7 +263,7 @@ bool PyramidROIAlign::canBroadcastInputAcrossBatch(int inputIndex) const // Configure the layer with input and output data types. void PyramidROIAlign::configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept { assert(supportsFormat(inputTypes[0], floatFormat)); check_valid_inputs(inputDims, nbInputs); @@ -284,9 +282,9 @@ void PyramidROIAlign::configurePlugin(const Dims* inputDims, int nbInputs, const // Attach the plugin object to an execution context and grant the plugin the access to some context resource. void PyramidROIAlign::attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) + cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept { } // Detach the plugin object from its execution context. -void PyramidROIAlign::detachFromContext() {} +void PyramidROIAlign::detachFromContext() noexcept {} diff --git a/plugin/pyramidROIAlignPlugin/pyramidROIAlignPlugin.h b/plugin/pyramidROIAlignPlugin/pyramidROIAlignPlugin.h index a252578d..54cdb97d 100644 --- a/plugin/pyramidROIAlignPlugin/pyramidROIAlignPlugin.h +++ b/plugin/pyramidROIAlignPlugin/pyramidROIAlignPlugin.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_PYRAMID_ROIALIGN_PLUGIN_H #define TRT_PYRAMID_ROIALIGN_PLUGIN_H @@ -42,51 +41,51 @@ public: ~PyramidROIAlign() override = default; - int getNbOutputs() const override; + int getNbOutputs() const noexcept override; - Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) override; + Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept override; - int initialize() override; + int initialize() noexcept override; - void terminate() override; + void terminate() noexcept override; - void destroy() override; + void destroy() noexcept override; - size_t getWorkspaceSize(int) const override; + size_t getWorkspaceSize(int) const noexcept override; - int enqueue( - int batch_size, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) override; + int enqueue(int batch_size, const void* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept override; - size_t getSerializationSize() const override; + size_t getSerializationSize() const noexcept override; - void serialize(void* buffer) const override; + void serialize(void* buffer) const noexcept override; - bool supportsFormat(DataType type, PluginFormat format) const override; + bool supportsFormat(DataType type, PluginFormat format) const noexcept override; - const char* getPluginType() const override; + const char* getPluginType() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - IPluginV2Ext* clone() const override; + IPluginV2Ext* clone() const noexcept override; - void setPluginNamespace(const char* libNamespace) override; + void setPluginNamespace(const char* libNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; - DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept override; - bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const override; + bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept override; - bool canBroadcastInputAcrossBatch(int inputIndex) const override; + bool canBroadcastInputAcrossBatch(int inputIndex) const noexcept override; void attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) override; + cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept override; void configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) override; + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept override; - void detachFromContext() override; + void detachFromContext() noexcept override; private: void check_valid_inputs(const nvinfer1::Dims* inputs, int nbInputDims); @@ -108,15 +107,15 @@ public: ~PyramidROIAlignPluginCreator(){}; - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const PluginFieldCollection* getFieldNames() override; + const PluginFieldCollection* getFieldNames() noexcept override; - IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) override; + IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; - IPluginV2Ext* deserializePlugin(const char* name, const void* data, size_t length) override; + IPluginV2Ext* deserializePlugin(const char* name, const void* data, size_t length) noexcept override; private: static PluginFieldCollection mFC; diff --git a/plugin/regionPlugin/regionPlugin.cpp b/plugin/regionPlugin/regionPlugin.cpp index 5e717478..bcc3fcb9 100644 --- a/plugin/regionPlugin/regionPlugin.cpp +++ b/plugin/regionPlugin/regionPlugin.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "regionPlugin.h" #include @@ -231,19 +230,20 @@ Region::Region(const void* buffer, size_t length) ASSERT(d == a + length); } -int Region::getNbOutputs() const +int Region::getNbOutputs() const noexcept { return 1; } -Dims Region::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) +Dims Region::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept { ASSERT(nbInputDims == 1); ASSERT(index == 0); return inputs[0]; } -int Region::enqueue(int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) +int Region::enqueue( + int batchSize, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { const void* inputData = inputs[0]; void* outputData = outputs[0]; @@ -257,11 +257,10 @@ int Region::enqueue(int batchSize, const void* const* inputs, void** outputs, vo } pluginStatus_t status = regionInference( stream, batchSize, C, H, W, num, coords, classes, hasSoftmaxTree, smTree.get(), inputData, outputData); - ASSERT(status == STATUS_SUCCESS); return status; } -size_t Region::getSerializationSize() const +size_t Region::getSerializationSize() const noexcept { // C, H, W, num, classes, coords, smTree !nullptr and other array members !nullptr, softmaxTree members size_t count = 6 * sizeof(int) + 8 * sizeof(bool); @@ -301,7 +300,7 @@ size_t Region::getSerializationSize() const return count; } -void Region::serialize(void* buffer) const +void Region::serialize(void* buffer) const noexcept { char *d = reinterpret_cast(buffer), *a = d; write(d, C); @@ -368,39 +367,41 @@ void Region::serialize(void* buffer) const ASSERT(d == a + getSerializationSize()); } -bool Region::supportsFormat(DataType type, PluginFormat format) const +bool Region::supportsFormat(DataType type, PluginFormat format) const noexcept { - return (type == DataType::kFLOAT && format == PluginFormat::kNCHW); + return (type == DataType::kFLOAT && format == PluginFormat::kLINEAR); } -int Region::initialize() +int Region::initialize() noexcept { return STATUS_SUCCESS; } -void Region::terminate() {} +void Region::terminate() noexcept +{ +} -const char* Region::getPluginType() const +const char* Region::getPluginType() const noexcept { return REGION_PLUGIN_NAME; } -const char* Region::getPluginVersion() const +const char* Region::getPluginVersion() const noexcept { return REGION_PLUGIN_VERSION; } -size_t Region::getWorkspaceSize(int maxBatchSize) const +size_t Region::getWorkspaceSize(int maxBatchSize) const noexcept { return 0; } -void Region::destroy() +void Region::destroy() noexcept { delete this; } -IPluginV2Ext* Region::clone() const +IPluginV2Ext* Region::clone() const noexcept { RegionParameters params{num, coords, classes, nullptr}; Region* plugin = new Region(params, C, H, W); @@ -411,31 +412,31 @@ IPluginV2Ext* Region::clone() const } // Set plugin namespace -void Region::setPluginNamespace(const char* pluginNamespace) +void Region::setPluginNamespace(const char* pluginNamespace) noexcept { mPluginNamespace = pluginNamespace; } -const char* Region::getPluginNamespace() const +const char* Region::getPluginNamespace() const noexcept { return mPluginNamespace.c_str(); } // Return the DataType of the plugin output at the requested index -DataType Region::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const +DataType Region::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { ASSERT(index == 0); return DataType::kFLOAT; } // Return true if output tensor is broadcast across a batch. -bool Region::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const +bool Region::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept { return false; } // Return true if plugin can use input that is broadcast across batch without replication. -bool Region::canBroadcastInputAcrossBatch(int inputIndex) const +bool Region::canBroadcastInputAcrossBatch(int inputIndex) const noexcept { return false; } @@ -443,9 +444,9 @@ bool Region::canBroadcastInputAcrossBatch(int inputIndex) const // Configure the layer with input and output data types. void Region::configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept { - ASSERT(*inputTypes == DataType::kFLOAT && floatFormat == PluginFormat::kNCHW); + ASSERT(*inputTypes == DataType::kFLOAT && floatFormat == PluginFormat::kLINEAR); ASSERT(nbInputs == 1); ASSERT(nbOutputs == 1); C = inputDims[0].d[0]; @@ -460,10 +461,10 @@ void Region::configurePlugin(const Dims* inputDims, int nbInputs, const Dims* ou } // Attach the plugin object to an execution context and grant the plugin the access to some context resource. -void Region::attachToContext(cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) {} +void Region::attachToContext(cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept {} // Detach the plugin object from its execution context. -void Region::detachFromContext() {} +void Region::detachFromContext() noexcept {} RegionPluginCreator::RegionPluginCreator() { @@ -476,22 +477,22 @@ RegionPluginCreator::RegionPluginCreator() mFC.fields = mPluginAttributes.data(); } -const char* RegionPluginCreator::getPluginName() const +const char* RegionPluginCreator::getPluginName() const noexcept { return REGION_PLUGIN_NAME; } -const char* RegionPluginCreator::getPluginVersion() const +const char* RegionPluginCreator::getPluginVersion() const noexcept { return REGION_PLUGIN_VERSION; } -const PluginFieldCollection* RegionPluginCreator::getFieldNames() +const PluginFieldCollection* RegionPluginCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2Ext* RegionPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2Ext* RegionPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { const PluginField* fields = fc->fields; for (int i = 0; i < fc->nbFields; ++i) @@ -525,7 +526,7 @@ IPluginV2Ext* RegionPluginCreator::createPlugin(const char* name, const PluginFi return obj; } -IPluginV2Ext* RegionPluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) +IPluginV2Ext* RegionPluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept { // This object will be deleted when the network is destroyed, which will // call Region::destroy() diff --git a/plugin/regionPlugin/regionPlugin.h b/plugin/regionPlugin/regionPlugin.h index fb5fa6a4..14346989 100644 --- a/plugin/regionPlugin/regionPlugin.h +++ b/plugin/regionPlugin/regionPlugin.h @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_REGION_PLUGIN_H #define TRT_REGION_PLUGIN_H #include "kernel.h" #include "plugin.h" #include -#include #include +#include namespace nvinfer1 { @@ -38,53 +37,53 @@ public: ~Region() override = default; - int getNbOutputs() const override; + int getNbOutputs() const noexcept override; - Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) override; + Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept override; - int initialize() override; + int initialize() noexcept override; - void terminate() override; + void terminate() noexcept override; - size_t getWorkspaceSize(int maxBatchSize) const override; + size_t getWorkspaceSize(int maxBatchSize) const noexcept override; - int enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) override; + int enqueue(int batchSize, const void* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept override; - size_t getSerializationSize() const override; + size_t getSerializationSize() const noexcept override; - void serialize(void* buffer) const override; + void serialize(void* buffer) const noexcept override; - bool supportsFormat(DataType type, PluginFormat format) const override; + bool supportsFormat(DataType type, PluginFormat format) const noexcept override; - const char* getPluginType() const override; + const char* getPluginType() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - void destroy() override; + void destroy() noexcept override; - IPluginV2Ext* clone() const override; + IPluginV2Ext* clone() const noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; - DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept override; - bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const override; + bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept override; - bool canBroadcastInputAcrossBatch(int inputIndex) const override; + bool canBroadcastInputAcrossBatch(int inputIndex) const noexcept override; void attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) override; + cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept override; void configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) override; + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept override; - void detachFromContext() override; + void detachFromContext() noexcept override; - void setSoftmaxTree(const std::shared_ptr& softmaxTree) + void setSoftmaxTree(const std::shared_ptr& softmaxTree) noexcept { smTree = softmaxTree; } @@ -106,15 +105,15 @@ public: ~RegionPluginCreator() override = default; - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const PluginFieldCollection* getFieldNames() override; + const PluginFieldCollection* getFieldNames() noexcept override; - IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) override; + IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; - IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; private: static PluginFieldCollection mFC; diff --git a/plugin/reorgPlugin/reorgPlugin.cpp b/plugin/reorgPlugin/reorgPlugin.cpp index 1d01cb5f..e7d5d91c 100644 --- a/plugin/reorgPlugin/reorgPlugin.cpp +++ b/plugin/reorgPlugin/reorgPlugin.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "reorgPlugin.h" using namespace nvinfer1; @@ -26,10 +25,10 @@ PluginFieldCollection ReorgPluginCreator::mFC{}; std::vector ReorgPluginCreator::mPluginAttributes; Reorg::Reorg(int C, int H, int W, int stride) - : C(C) - , H(H) - , W(W) - , stride(stride) + : C(C), + H(H), + W(W), + stride(stride) { } @@ -48,34 +47,34 @@ Reorg::Reorg(const void* buffer, size_t length) ASSERT(d == a + length); } -int Reorg::getNbOutputs() const +int Reorg::getNbOutputs() const noexcept { return 1; } -Dims Reorg::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) +Dims Reorg::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept { ASSERT(nbInputDims == 1); ASSERT(index == 0); - return DimsCHW(inputs[0].d[0] * stride * stride, inputs[0].d[1] / stride, inputs[0].d[2] / stride); + return Dims3(inputs[0].d[0] * stride * stride, inputs[0].d[1] / stride, inputs[0].d[2] / stride); } -int Reorg::enqueue(int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) +int Reorg::enqueue( + int batchSize, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { const void* inputData = inputs[0]; void* outputData = outputs[0]; - pluginStatus_t status = reorgInference(stream, batchSize, C, H, W, stride, inputData, outputData); - ASSERT(status == STATUS_SUCCESS); + pluginStatus_t status = reorgInference(stream, batchSize, C, H, W, stride, inputData, outputData); return status; } -size_t Reorg::getSerializationSize() const +size_t Reorg::getSerializationSize() const noexcept { // C, H, W, stride return sizeof(int) * 4; } -void Reorg::serialize(void* buffer) const +void Reorg::serialize(void* buffer) const noexcept { char *d = reinterpret_cast(buffer), *a = d; write(d, C); @@ -85,51 +84,51 @@ void Reorg::serialize(void* buffer) const ASSERT(d == a + getSerializationSize()); } -bool Reorg::supportsFormat(DataType type, PluginFormat format) const +bool Reorg::supportsFormat(DataType type, PluginFormat format) const noexcept { - return (type == DataType::kFLOAT && format == PluginFormat::kNCHW); + return (type == DataType::kFLOAT && format == PluginFormat::kLINEAR); } -int Reorg::initialize() +int Reorg::initialize() noexcept { return STATUS_SUCCESS; } -void Reorg::terminate() {} +void Reorg::terminate() noexcept {} -size_t Reorg::getWorkspaceSize(int maxBatchSize) const +size_t Reorg::getWorkspaceSize(int maxBatchSize) const noexcept { return 0; } -const char* Reorg::getPluginType() const +const char* Reorg::getPluginType() const noexcept { return REORG_PLUGIN_NAME; } -const char* Reorg::getPluginVersion() const +const char* Reorg::getPluginVersion() const noexcept { return REORG_PLUGIN_VERSION; } -void Reorg::destroy() +void Reorg::destroy() noexcept { delete this; } // Set plugin namespace -void Reorg::setPluginNamespace(const char* pluginNamespace) +void Reorg::setPluginNamespace(const char* pluginNamespace) noexcept { mPluginNamespace = pluginNamespace; } -const char* Reorg::getPluginNamespace() const +const char* Reorg::getPluginNamespace() const noexcept { return mPluginNamespace.c_str(); } // Return the DataType of the plugin output at the requested index -DataType Reorg::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const +DataType Reorg::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { // Only 1 input and 1 output from the plugin layer ASSERT(index == 0); @@ -139,13 +138,13 @@ DataType Reorg::getOutputDataType(int index, const nvinfer1::DataType* inputType } // Return true if output tensor is broadcast across a batch. -bool Reorg::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const +bool Reorg::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept { return false; } // Return true if plugin can use input that is broadcast across batch without replication. -bool Reorg::canBroadcastInputAcrossBatch(int inputIndex) const +bool Reorg::canBroadcastInputAcrossBatch(int inputIndex) const noexcept { return false; } @@ -153,9 +152,9 @@ bool Reorg::canBroadcastInputAcrossBatch(int inputIndex) const // Configure the layer with input and output data types. void Reorg::configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept { - ASSERT(*inputTypes == DataType::kFLOAT && floatFormat == PluginFormat::kNCHW); + ASSERT(*inputTypes == DataType::kFLOAT && floatFormat == PluginFormat::kLINEAR); ASSERT(nbInputs == 1); ASSERT(nbOutputs == 1); ASSERT(stride > 0); @@ -167,12 +166,12 @@ void Reorg::configurePlugin(const Dims* inputDims, int nbInputs, const Dims* out } // Attach the plugin object to an execution context and grant the plugin the access to some context resource. -void Reorg::attachToContext(cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) {} +void Reorg::attachToContext(cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept {} // Detach the plugin object from its execution context. -void Reorg::detachFromContext() {} +void Reorg::detachFromContext() noexcept {} -IPluginV2Ext* Reorg::clone() const +IPluginV2Ext* Reorg::clone() const noexcept { IPluginV2Ext* plugin = new Reorg(C, H, W, stride); plugin->setPluginNamespace(mPluginNamespace.c_str()); @@ -187,22 +186,22 @@ ReorgPluginCreator::ReorgPluginCreator() mFC.fields = mPluginAttributes.data(); } -const char* ReorgPluginCreator::getPluginName() const +const char* ReorgPluginCreator::getPluginName() const noexcept { return REORG_PLUGIN_NAME; } -const char* ReorgPluginCreator::getPluginVersion() const +const char* ReorgPluginCreator::getPluginVersion() const noexcept { return REORG_PLUGIN_VERSION; } -const PluginFieldCollection* ReorgPluginCreator::getFieldNames() +const PluginFieldCollection* ReorgPluginCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2Ext* ReorgPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2Ext* ReorgPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { const PluginField* fields = fc->fields; ASSERT(fc->nbFields == 1); @@ -214,7 +213,7 @@ IPluginV2Ext* ReorgPluginCreator::createPlugin(const char* name, const PluginFie return obj; } -IPluginV2Ext* ReorgPluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) +IPluginV2Ext* ReorgPluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept { // This object will be deleted when the network is destroyed, which will // call ReorgPlugin::destroy() diff --git a/plugin/reorgPlugin/reorgPlugin.h b/plugin/reorgPlugin/reorgPlugin.h index efa85bcb..5500b368 100644 --- a/plugin/reorgPlugin/reorgPlugin.h +++ b/plugin/reorgPlugin/reorgPlugin.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_REORG_PLUGIN_H #define TRT_REORG_PLUGIN_H #include "kernel.h" @@ -37,51 +36,51 @@ public: ~Reorg() override = default; - int getNbOutputs() const override; + int getNbOutputs() const noexcept override; - Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) override; + Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept override; - int initialize() override; + int initialize() noexcept override; - void terminate() override; + void terminate() noexcept override; - size_t getWorkspaceSize(int maxBatchSize) const override; + size_t getWorkspaceSize(int maxBatchSize) const noexcept override; - int enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) override; + int enqueue(int batchSize, const void* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept override; - size_t getSerializationSize() const override; + size_t getSerializationSize() const noexcept override; - void serialize(void* buffer) const override; + void serialize(void* buffer) const noexcept override; - bool supportsFormat(DataType type, PluginFormat format) const override; + bool supportsFormat(DataType type, PluginFormat format) const noexcept override; - const char* getPluginType() const override; + const char* getPluginType() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - void destroy() override; + void destroy() noexcept override; - IPluginV2Ext* clone() const override; + IPluginV2Ext* clone() const noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; - DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept override; - bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const override; + bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept override; - bool canBroadcastInputAcrossBatch(int inputIndex) const override; + bool canBroadcastInputAcrossBatch(int inputIndex) const noexcept override; void attachToContext( - cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) override; + cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept override; void configurePlugin(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, const DataType* inputTypes, const DataType* outputTypes, const bool* inputIsBroadcast, - const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) override; + const bool* outputIsBroadcast, PluginFormat floatFormat, int maxBatchSize) noexcept override; - void detachFromContext() override; + void detachFromContext() noexcept override; private: int C{}, H{}, W{}; @@ -96,15 +95,15 @@ public: ~ReorgPluginCreator() override = default; - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const PluginFieldCollection* getFieldNames() override; + const PluginFieldCollection* getFieldNames() noexcept override; - IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) override; + IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; - IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; private: static PluginFieldCollection mFC; diff --git a/plugin/resizeNearestPlugin/resizeNearestPlugin.cpp b/plugin/resizeNearestPlugin/resizeNearestPlugin.cpp index 19384494..8d294dcf 100644 --- a/plugin/resizeNearestPlugin/resizeNearestPlugin.cpp +++ b/plugin/resizeNearestPlugin/resizeNearestPlugin.cpp @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "resizeNearestPlugin.h" #include "plugin.h" -#include #include +#include #include #define DEBUG 0 @@ -116,7 +115,9 @@ int ResizeNearest::initialize() noexcept return 0; } -void ResizeNearest::terminate() noexcept {} +void ResizeNearest::terminate() noexcept +{ +} void ResizeNearest::destroy() noexcept { @@ -191,11 +192,11 @@ const char* ResizeNearest::getPluginNamespace() const noexcept bool ResizeNearest::supportsFormat(DataType type, PluginFormat format) const noexcept { - return (type == DataType::kFLOAT && format == PluginFormat::kNCHW); + return (type == DataType::kFLOAT && format == PluginFormat::kLINEAR); } int ResizeNearest::enqueue( - int batch_size, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) noexcept + int batch_size, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { int nchan = mOutputDims.d[0]; diff --git a/plugin/resizeNearestPlugin/resizeNearestPlugin.h b/plugin/resizeNearestPlugin/resizeNearestPlugin.h index 8bafa74d..f51e86df 100644 --- a/plugin/resizeNearestPlugin/resizeNearestPlugin.h +++ b/plugin/resizeNearestPlugin/resizeNearestPlugin.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_RESIZENEAREST_PLUGIN_H #define TRT_RESIZENEAREST_PLUGIN_H @@ -52,8 +51,8 @@ public: size_t getWorkspaceSize(int) const noexcept override; - int enqueue( - int batch_size, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) noexcept override; + int enqueue(int batch_size, const void* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept override; size_t getSerializationSize() const noexcept override; diff --git a/samples/opensource/samplePlugin/CMakeLists.txt b/plugin/scatterPlugin/CMakeLists.txt similarity index 70% rename from samples/opensource/samplePlugin/CMakeLists.txt rename to plugin/scatterPlugin/CMakeLists.txt index 559b0cf7..53b70a7e 100644 --- a/samples/opensource/samplePlugin/CMakeLists.txt +++ b/plugin/scatterPlugin/CMakeLists.txt @@ -13,11 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # -SET(SAMPLE_SOURCES - samplePlugin.cpp -) - -set(SAMPLE_PARSERS "caffe") -set(PLUGINS_NEEDED ON) - -include(../../CMakeSamplesTemplate.txt) +file(GLOB SRCS *.cpp) +set(PLUGIN_SOURCES ${PLUGIN_SOURCES} ${SRCS}) +set(PLUGIN_SOURCES ${PLUGIN_SOURCES} PARENT_SCOPE) +file(GLOB CU_SRCS *.cu) +set(PLUGIN_CU_SOURCES ${PLUGIN_CU_SOURCES} ${CU_SRCS}) +set(PLUGIN_CU_SOURCES ${PLUGIN_CU_SOURCES} PARENT_SCOPE) diff --git a/plugin/scatterPlugin/scatterLayer.cu b/plugin/scatterPlugin/scatterLayer.cu new file mode 100644 index 00000000..f0460c12 --- /dev/null +++ b/plugin/scatterPlugin/scatterLayer.cu @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * + * 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 "kernel.h" + +#define CUBLAS_CHECK(condition) \ + do \ + { \ + cublasStatus_t status = condition; \ + if (status != CUBLAS_STATUS_SUCCESS) \ + { \ + printf("%s %d CUBLAS FAIL %s\n", __FILE__, __LINE__, cublasGetErrorString(status)); \ + } \ + } while (0) + +//this scatter kernel works on a 2d table writing rows +//index is 1-D array +//updates is 2-D array +//output is 2-D array +//output[index[i]] = updates[i] +__global__ void scatterKernel( + char* output, + const char* updates, + const int* indices, + int pitch, + int rowSize) +{ + int idx = indices[blockIdx.x]; + char* pDst = (char*)output + idx * pitch; + const char* pSrc = updates + blockIdx.x * rowSize; + memcpy(pDst, pSrc, rowSize); +} + +// Transform nd index to 1 - d index +__global__ void transformIdxKernel( + int* output, + const int* transformCoeff, // these are actually the output pitches of the respective dimensions + const int* indices, + int sliceRank) +{ + const int* idx = indices + sliceRank * blockIdx.x; + int transformedIdx = 0; + for (int i = 0; i < sliceRank; i++) + { + transformedIdx += idx[i] * transformCoeff[i]; + } + output[blockIdx.x] = transformedIdx; +} + + +pluginStatus_t scatterNDInference( + cudaStream_t stream, + int* transformCoeff, + int nOutputDims, + int sliceRank, + int nRows, + int rowSize, + int copySize, + int sizeOfElementInBytes, + const void* index, + const void* updates, + const void* data, + void* output, + void* workspace) +{ + const int* _index = (const int*)(index); + const char* _updates = (const char*)(updates); + char* _output = (char*)(output); + int* wo = (int*)(workspace); + int* transformedIdx = wo + sizeof(int)*nOutputDims; + int* deviceTransformCoeff = wo; + cudaMemcpy(workspace, transformCoeff, sizeof(int)*nOutputDims,cudaMemcpyHostToDevice ); + transformIdxKernel<<>>(transformedIdx, deviceTransformCoeff, _index, sliceRank); + cudaMemcpy(output, data, copySize, cudaMemcpyDeviceToDevice); + //assuming output pitch = rowSize i.e no padding + scatterKernel<<>>(_output, _updates, transformedIdx, rowSize*4, rowSize*4); + return STATUS_SUCCESS; +} \ No newline at end of file diff --git a/plugin/scatterPlugin/scatterPlugin.cpp b/plugin/scatterPlugin/scatterPlugin.cpp new file mode 100644 index 00000000..ed5bc2ca --- /dev/null +++ b/plugin/scatterPlugin/scatterPlugin.cpp @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * + * 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 "scatterPlugin.h" +#include "half.h" +#include +#include +#include +#include +#include + +using namespace nvinfer1; + +using nvinfer1::plugin::ScatterND; +using nvinfer1::plugin::ScatterNDPluginCreator; + +namespace +{ + +const char* SCATTERND_PLUGIN_VERSION{"1"}; +const char* SCATTERND_PLUGIN_NAME{"ScatterND"}; +} // namespace + +PluginFieldCollection ScatterNDPluginCreator::mFC{}; + +ScatterND::ScatterND() +{ + +} + +int ScatterND::getNbOutputs() const noexcept +{ + // Plugin layer has 1 output + return 1; +} + +DimsExprs ScatterND::getOutputDimensions(int32_t outputIndex, const DimsExprs* inputs, int32_t nbInputs, IExprBuilder& exprBuilder) noexcept +{ + //output should have same dimensions as data tensor + DimsExprs ret = inputs[dataTensorIdx]; + return ret; +} + +int ScatterND::initialize() noexcept +{ + return 0; +} + +void ScatterND::terminate() noexcept +{ +} + +bool ScatterND::supportsFormatCombination(int32_t pos, const PluginTensorDesc* inOut, int32_t nbInputs, int32_t nbOutputs) noexcept +{ + ASSERT(pos < 4); + ASSERT(nbInputs == 3); + ASSERT(nbOutputs == 1); + const PluginTensorDesc& desc = inOut[pos]; + bool ret = false; + switch (pos) + { + case dataTensorIdx: + case updateTensorIdx: + ret = ((desc.type == DataType::kFLOAT || desc.type == DataType::kINT32) + && desc.format == TensorFormat::kLINEAR); + break; + case indexTensorIdx: + ret = (desc.type == DataType::kINT32 && desc.format == TensorFormat::kLINEAR); + break; + case 3: + ret = ((desc.type == DataType::kFLOAT || desc.type == DataType::kINT32) && desc.format == TensorFormat::kLINEAR); + break; + } + return ret; +} + +void ScatterND::configurePlugin(const DynamicPluginTensorDesc* in, int32_t nbInputs, const DynamicPluginTensorDesc* out, int32_t nbOutputs) noexcept +{ + +} + +int32_t ScatterND::calculateNumSlices(Dims indexTensorDims) const noexcept +{ + int32_t nSlices = 1; + for (int i = 0; i < indexTensorDims.nbDims-1; i++) + { + nSlices *= indexTensorDims.d[i]; + } + return nSlices; +} + +size_t ScatterND::getWorkspaceSize(const PluginTensorDesc* inputs, int32_t nbInputs, const PluginTensorDesc* outputs,int32_t nbOutputs) const noexcept +{ + int32_t nSlices = calculateNumSlices(inputs[indexTensorIdx].dims); + //transformCoeffs + transformed indices + return outputs[0].dims.MAX_DIMS * sizeof(int32_t) + nSlices * sizeof(int32_t); +} + +void ScatterND::calculateTransformCoeff(const Dims& dataTensorDims, int indexRank, int32_t* transformCoeff) const noexcept +{ + std::vector pitches; + for (int32_t i = indexRank - 1, nIndx = 1; i >= 0 ; i--) + { + pitches.push_back(nIndx); + nIndx *= dataTensorDims.d[i]; + } + + std::reverse(pitches.begin(), pitches.end()); //last dimension pitch is always one (assuming linear mem) + + std::copy(pitches.begin(), pitches.end(), transformCoeff); + +} + +int32_t ScatterND::calculateCopySize(const Dims& dataDims) const noexcept +{ + int32_t copySize = 1; + for (int i = 0; i < dataDims.nbDims; i++) + { + copySize *= dataDims.d[i]; + } + copySize *= sizeof(float); + return copySize; +} + +int32_t ScatterND::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept +{ + int32_t transformCoeff[outputDesc[0].dims.MAX_DIMS]; + std::memset(transformCoeff, 0, sizeof(int32_t)*outputDesc[0].dims.MAX_DIMS); + Dims IndexDims = inputDesc[indexTensorIdx].dims; + + Dims dataDims = inputDesc[dataTensorIdx].dims; + + int32_t indexRank = IndexDims.d[IndexDims.nbDims-1]; + ASSERT(indexRank <= dataDims.nbDims); + + int32_t nSlices = calculateNumSlices(IndexDims); + int32_t rowSize = 1; + int32_t copySize = calculateCopySize(dataDims); + int32_t elementSizeInBytes = 1; + switch (inputDesc->type) + { + case DataType::kFLOAT: + case DataType::kINT32: + elementSizeInBytes = 4; + break; + case DataType::kHALF: + elementSizeInBytes = 2; + break; + case DataType::kINT8: + case DataType::kBOOL: + elementSizeInBytes = 1; + break; + } + + for (int i = indexRank; i < dataDims.nbDims; i++) + { + rowSize *= dataDims.d[i]; + } + + calculateTransformCoeff(dataDims, indexRank, transformCoeff); + + scatterNDInference(stream, transformCoeff, + dataDims.nbDims, + indexRank, + nSlices, + rowSize, + copySize, + elementSizeInBytes, + inputs[indexTensorIdx], + inputs[updateTensorIdx], + inputs[dataTensorIdx], + outputs[0], + workspace ); + + return 0; +} + +size_t ScatterND::getSerializationSize() const noexcept +{ + + return 0; +} + +void ScatterND::serialize(void* buffer) const noexcept +{ + return; +} + + + +// Set plugin namespace +void ScatterND::setPluginNamespace(const char* pluginNamespace) noexcept +{ + mPluginNamespace = pluginNamespace; +} + +const char* ScatterND::getPluginNamespace() const noexcept +{ + return mPluginNamespace.c_str(); +} + +// Return the DataType of the plugin output at the requested index +DataType ScatterND::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept +{ + ASSERT(index == 0); + return inputTypes[dataTensorIdx]; +} + +// Attach the plugin object to an execution context and grant the plugin the access to some context resource. +void ScatterND::attachToContext(cudnnContext* cudnn, cublasContext* cublas, IGpuAllocator* gpuAllocator) noexcept +{ + return; +} + +// Detach the plugin object from its execution context. +void ScatterND::detachFromContext() noexcept {} + +const char* ScatterND::getPluginType() const noexcept +{ + return SCATTERND_PLUGIN_NAME; +} + +const char* ScatterND::getPluginVersion() const noexcept +{ + return SCATTERND_PLUGIN_VERSION; +} + +void ScatterND::destroy() noexcept +{ + delete this; +} + +// Clone the plugin +IPluginV2DynamicExt* ScatterND::clone() const noexcept +{ + // Create a new instance + ScatterND* plugin = new ScatterND(); + plugin->setPluginNamespace(mPluginNamespace.c_str()); + return plugin; +} + +ScatterNDPluginCreator::ScatterNDPluginCreator() +{ + mFC.nbFields = 0; +} + +const char* ScatterNDPluginCreator::getPluginName() const noexcept +{ + return SCATTERND_PLUGIN_NAME; +} + +const char* ScatterNDPluginCreator::getPluginVersion() const noexcept +{ + return SCATTERND_PLUGIN_VERSION; +} + +const PluginFieldCollection* ScatterNDPluginCreator::getFieldNames() noexcept +{ + return &mFC; +} + +IPluginV2Ext* ScatterNDPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept +{ + ScatterND* obj = new ScatterND(); + obj->setPluginNamespace(mNamespace.c_str()); + return obj; +} + +IPluginV2Ext* ScatterNDPluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept +{ + // This object will be deleted when the network is destroyed, which will + // call Normalize::destroy() + ScatterND* obj = new ScatterND(); + obj->setPluginNamespace(mNamespace.c_str()); + return obj; +} diff --git a/plugin/scatterPlugin/scatterPlugin.h b/plugin/scatterPlugin/scatterPlugin.h new file mode 100644 index 00000000..b7d22a1d --- /dev/null +++ b/plugin/scatterPlugin/scatterPlugin.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ +#ifndef TRT_SCATTER_PLUGIN_H +#define TRT_SCATTER_PLUGIN_H +#include "cudnn.h" +#include "kernel.h" +#include "plugin.h" +#include +#include +#include + +namespace nvinfer1 +{ +namespace plugin +{ + +class ScatterND : public IPluginV2DynamicExt +{ +public: + ScatterND(); + + ~ScatterND() override = default; + + int getNbOutputs() const noexcept override; + + DimsExprs getOutputDimensions( + int32_t outputIndex, const DimsExprs* inputs, int32_t nbInputs, IExprBuilder& exprBuilder) noexcept override; + + bool supportsFormatCombination( + int32_t pos, const PluginTensorDesc* inOut, int32_t nbInputs, int32_t nbOutputs) noexcept override; + + void configurePlugin(const DynamicPluginTensorDesc* in, int32_t nbInputs, + const DynamicPluginTensorDesc* out, int32_t nbOutputs) noexcept override; + + virtual size_t getWorkspaceSize(const PluginTensorDesc* inputs, int32_t nbInputs, const PluginTensorDesc* outputs, + int32_t nbOutputs) const noexcept override; + + int initialize() noexcept override; + + void terminate() noexcept override; + + int32_t enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; + + size_t getSerializationSize() const noexcept override; + + void serialize(void* buffer) const noexcept override; + + const char* getPluginType() const noexcept override; + + const char* getPluginVersion() const noexcept override; + + void destroy() noexcept override; + + IPluginV2DynamicExt* clone() const noexcept override; + + void setPluginNamespace(const char* pluginNamespace) noexcept override; + + const char* getPluginNamespace() const noexcept override; + + DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept override; + + void attachToContext( + cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept override; + + void detachFromContext() noexcept override; +private: + + //calculate how many slices we need to scatter = reduce_mul(indexTensor.shape[:-1]) + int32_t calculateNumSlices(Dims indexTensorDims) const noexcept; + int32_t calculateCopySize(const Dims& dataDims) const noexcept; + void calculateTransformCoeff(const Dims& dataTensorDims, int indexRank, int32_t* transformCoeff) const noexcept; + std::string mPluginNamespace; + + static constexpr int indexTensorIdx = 1; + static constexpr int updateTensorIdx = 2; + static constexpr int dataTensorIdx = 0; +}; + +class ScatterNDPluginCreator : public BaseCreator +{ +public: + ScatterNDPluginCreator(); + + ~ScatterNDPluginCreator() override = default; + + const char* getPluginName() const noexcept override; + + const char* getPluginVersion()const noexcept override; + + const PluginFieldCollection* getFieldNames() noexcept override; + + IPluginV2Ext* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override; + + IPluginV2Ext* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; + +private: + static PluginFieldCollection mFC; +}; +} // namespace plugin +} // namespace nvinfer1 + +#endif // TRT_SCATTER_PLUGIN_H diff --git a/plugin/skipLayerNormPlugin/skipLayerNormInt8InterleavedKernel.cu b/plugin/skipLayerNormPlugin/skipLayerNormInt8InterleavedKernelHFace.cu similarity index 65% rename from plugin/skipLayerNormPlugin/skipLayerNormInt8InterleavedKernel.cu rename to plugin/skipLayerNormPlugin/skipLayerNormInt8InterleavedKernelHFace.cu index ed675601..574f593a 100644 --- a/plugin/skipLayerNormPlugin/skipLayerNormInt8InterleavedKernel.cu +++ b/plugin/skipLayerNormPlugin/skipLayerNormInt8InterleavedKernelHFace.cu @@ -40,9 +40,9 @@ inline __device__ void res_add( hdata[3] = float(idata4.w) * dqData + float(ires4.w) * dqRes; } -template -__global__ void skipln_vec32(const int8_t* input, const int8_t* skip, int8_t* output, const half* beta, - const half* gamma, const float dqScaleIn, const float dqScaleSkip, const float qScale, const int total) +template +__global__ void skipln_vec32_hface(const int8_t* input, const int8_t* skip, int8_t* output, const half* beta, + const half* gamma, const float dqScaleIn, const float dqScaleSkip, const float qScale, const int32_t total) { // clang-format off @@ -69,30 +69,30 @@ __global__ void skipln_vec32(const int8_t* input, const int8_t* skip, int8_t* ou __shared__ half2 smem_red[VECS_PER_CTA][WARPS]; constexpr float rld = 1.f / (float(HEADS) * float(HEAD_SIZE)); - const int bidx = blockIdx.x; - const int tidx = threadIdx.x; - const int row = tidx / THREADS_PER_ROW; - const int col = tidx % THREADS_PER_ROW; - const int lane = tidx % 32; - const int warp = tidx / 32; + const int32_t bidx = blockIdx.x; + const int32_t tidx = threadIdx.x; + const int32_t row = tidx / THREADS_PER_ROW; + const int32_t col = tidx % THREADS_PER_ROW; + const int32_t lane = tidx % 32; + const int32_t warp = tidx / 32; const bool is_warp_lead = (lane < THREADS_PER_ROW) && ((lane & 1) == 0); const bool is_cta_lead = (tidx < THREADS_PER_ROW) && ((tidx & 1) == 0); // token position: every two threads load together the 32B at one token // position - const int pos = col / 2; + const int32_t pos = col / 2; - const int pos_offset = bidx * VECS_PER_CTA + pos; // for token positions per block, disabling 2 threads per pos + const int32_t pos_offset = bidx * VECS_PER_CTA + pos; // for token positions per block, disabling 2 threads per pos const bool my_pred = pos_offset < total; - const int row_stride_bytes = total * 32; + const int32_t row_stride_bytes = total * 32; uint4 in_data[LDGS]; uint4 in_skip[LDGS]; float hdata[LDGS * 4][4]; - const int gmem_offset = row * row_stride_bytes + (bidx * THREADS_PER_ROW + col) * BYTES_PER_LDG; + const int32_t gmem_offset = row * row_stride_bytes + (bidx * THREADS_PER_ROW + col) * BYTES_PER_LDG; #pragma unroll - for (int ii = 0; ii < LDGS; ii++) + for (int32_t ii = 0; ii < LDGS; ii++) { in_data[ii] = {0, 0, 0, 0}; in_skip[ii] = {0, 0, 0, 0}; @@ -114,7 +114,7 @@ __global__ void skipln_vec32(const int8_t* input, const int8_t* skip, int8_t* ou half* b = reinterpret_cast(&smem_[0]); half* g = reinterpret_cast(&smem_[PARAM_BYTES]); #pragma unroll - for (int ii = 0; ii < LDGS; ii++) + for (int32_t ii = 0; ii < LDGS; ii++) { res_add(hdata[ii * 4 + 0], in_data[ii].x, in_skip[ii].x, dqScaleIn, dqScaleSkip); res_add(hdata[ii * 4 + 1], in_data[ii].y, in_skip[ii].y, dqScaleIn, dqScaleSkip); @@ -125,10 +125,10 @@ __global__ void skipln_vec32(const int8_t* input, const int8_t* skip, int8_t* ou half2 stats_local = {0, 0}; #pragma unroll - for (int ii = 0; ii < LDGS * 4; ii++) + for (int32_t ii = 0; ii < LDGS * 4; ii++) { #pragma unroll - for (int jj = 0; jj < 4; jj++) + for (int32_t jj = 0; jj < 4; jj++) { const float tmp = hdata[ii][jj] * (rld); stats_local = stats_local + __floats2half2_rn(tmp, tmp * hdata[ii][jj]); @@ -137,7 +137,7 @@ __global__ void skipln_vec32(const int8_t* input, const int8_t* skip, int8_t* ou stats_local = stats_local + __shfl_xor_sync(uint32_t(-1), stats_local, 1); __syncwarp(); if (VECS_PER_CTA == 1) - { + { stats_local = stats_local + __shfl_xor_sync(uint32_t(-1), stats_local, 2); __syncwarp(); stats_local = stats_local + __shfl_xor_sync(uint32_t(-1), stats_local, 4); __syncwarp(); } @@ -145,7 +145,7 @@ __global__ void skipln_vec32(const int8_t* input, const int8_t* skip, int8_t* ou { stats_local = stats_local + __shfl_xor_sync(uint32_t(-1), stats_local, 4); __syncwarp(); } - + stats_local = stats_local + __shfl_xor_sync(uint32_t(-1), stats_local, 8); __syncwarp(); stats_local = stats_local + __shfl_xor_sync(uint32_t(-1), stats_local, 16); __syncwarp(); @@ -158,7 +158,7 @@ __global__ void skipln_vec32(const int8_t* input, const int8_t* skip, int8_t* ou if (is_cta_lead) { - for (int ii = 1; ii < WARPS; ii++) + for (int32_t ii = 1; ii < WARPS; ii++) { stats_local = stats_local + smem_red[pos][ii]; } @@ -174,15 +174,15 @@ __global__ void skipln_vec32(const int8_t* input, const int8_t* skip, int8_t* ou const float2 statsf = __half22float2(smem_red[pos][0]); #pragma unroll - for (int ii = 0; ii < LDGS; ii++) + for (int32_t ii = 0; ii < LDGS; ii++) { #pragma unroll - for (int jj = 0; jj < 4; jj++) + for (int32_t jj = 0; jj < 4; jj++) { #pragma unroll - for (int kk = 0; kk < 4; kk++) + for (int32_t kk = 0; kk < 4; kk++) { - const int param_idx = (ii * ROWS_PER_LDG + row) * 32 + (jj * 4 + kk) + (tidx & 1) * 16; + const int32_t param_idx = (ii * ROWS_PER_LDG + row) * 32 + (jj * 4 + kk) + (tidx & 1) * 16; const float bb = b[param_idx]; const float gg = g[param_idx]; hdata[ii * 4 + jj][kk] = gg * statsf.y * (hdata[ii * 4 + jj][kk] - statsf.x) + bb; @@ -190,9 +190,8 @@ __global__ void skipln_vec32(const int8_t* input, const int8_t* skip, int8_t* ou } } - #pragma unroll - for (int ii = 0; ii < LDGS; ii++) + for (int32_t ii = 0; ii < LDGS; ii++) { in_data[ii].x = pack4(hdata[ii * 4 + 0], qScale); in_data[ii].y = pack4(hdata[ii * 4 + 1], qScale); @@ -201,7 +200,7 @@ __global__ void skipln_vec32(const int8_t* input, const int8_t* skip, int8_t* ou } #pragma unroll - for (int ii = 0; ii < LDGS; ii++) + for (int32_t ii = 0; ii < LDGS; ii++) { if (my_pred) { @@ -211,57 +210,60 @@ __global__ void skipln_vec32(const int8_t* input, const int8_t* skip, int8_t* ou // store } -void launch_large(cudaStream_t stream, const int ld, const int total, const int8_t* input, const int8_t* skip, - const half* beta, const half* gamma, int8_t* output, const float dqScaleIn, const float dqScaleSkip, - const float qScale) +int32_t launch_large_hface(cudaStream_t stream, const int32_t ld, const int32_t total, const int8_t* input, + const int8_t* skip, const half* beta, const half* gamma, int8_t* output, const float dqScaleIn, + const float dqScaleSkip, const float qScale) { if (ld == 1024) { - constexpr int WARPS = 4; - constexpr int THREADS_PER_ROW = 8; - constexpr int HEADS = 16; - constexpr int PARAM_BYTES = HEADS * 64 * 2 * sizeof(half); - constexpr int VECS_PER_CTA = THREADS_PER_ROW / 2; - const int blocks = (total + VECS_PER_CTA - 1) / VECS_PER_CTA; + constexpr int32_t WARPS = 4; + constexpr int32_t THREADS_PER_ROW = 8; + constexpr int32_t HEADS = 16; + constexpr int32_t PARAM_BYTES = HEADS * 64 * 2 * sizeof(half); + constexpr int32_t VECS_PER_CTA = THREADS_PER_ROW / 2; + const int32_t blocks = (total + VECS_PER_CTA - 1) / VECS_PER_CTA; - skipln_vec32<<>>( + skipln_vec32_hface<<>>( input, skip, output, beta, gamma, dqScaleIn, dqScaleSkip, qScale, total); } else if (ld == 768) { - constexpr int WARPS = 3; - constexpr int THREADS_PER_ROW = 8; - constexpr int HEADS = 12; - constexpr int PARAM_BYTES = HEADS * 64 * 2 * sizeof(half); - constexpr int VECS_PER_CTA = THREADS_PER_ROW / 2; - const int blocks = (total + VECS_PER_CTA - 1) / VECS_PER_CTA; + constexpr int32_t WARPS = 3; + constexpr int32_t THREADS_PER_ROW = 8; + constexpr int32_t HEADS = 12; + constexpr int32_t PARAM_BYTES = HEADS * 64 * 2 * sizeof(half); + constexpr int32_t VECS_PER_CTA = THREADS_PER_ROW / 2; + const int32_t blocks = (total + VECS_PER_CTA - 1) / VECS_PER_CTA; - skipln_vec32<<>>( + skipln_vec32_hface<<>>( input, skip, output, beta, gamma, dqScaleIn, dqScaleSkip, qScale, total); } else { - ASSERT(false); + return STATUS_FAILURE; } + + return cudaPeekAtLastError(); } // naive kernel that only changes the addressing seems to be faster for small problem sizes -template -__global__ void skiplnDQQ_vec(const int ld, const int8_t* input, const int8_t* skip, int8_t* output, const half* beta, - const half* gamma, const float dqScaleIn, const float dqScaleSkip, const float qScale, const int total) +template +__global__ void skiplnDQQ_vec3(const int32_t ld, const int8_t* input, const int8_t* skip, int8_t* output, + const half* beta, const half* gamma, const float dqScaleIn, const float dqScaleSkip, const float qScale, + const int32_t total) { - const int hinner = threadIdx.x % 4; - const int houter = threadIdx.x / 4; + const int32_t hinner = threadIdx.x % 4; + const int32_t houter = threadIdx.x / 4; - const int tidx = threadIdx.x; - const int bidx = blockIdx.x; - const int idx = houter * total * 32 + bidx * 32 + hinner * VPT; + const int32_t tidx = threadIdx.x; + const int32_t bidx = blockIdx.x; + const int32_t idx = houter * total * 32 + bidx * 32 + hinner * VPT; // 4 * 1024 * 4 * 2 Bytes = 16KB per block int8_t in_local[VPT]; int8_t skip_local[VPT]; - half in_local_dq[VPT]; // dequantized input + skip - half beta_local[VPT]; + half in_local_dq[VPT]; // dequantized input + skip + half beta_local[VPT]; half gamma_local[VPT]; // load input tensors @@ -276,7 +278,7 @@ __global__ void skiplnDQQ_vec(const int ld, const int8_t* input, const int8_t* s const half rld = half(1.f) / half(ld); #pragma unroll - for (int it = 0; it < VPT; it++) + for (int32_t it = 0; it < VPT; it++) { // DQ input and skip const float tmp_in = in_local[it]; @@ -303,41 +305,48 @@ __global__ void skiplnDQQ_vec(const int ld, const int8_t* input, const int8_t* s __syncthreads(); + static_assert(VPT % 4 == 0, ""); + uint32_t out_local[VPT/4]; #pragma unroll - for (int it = 0; it < VPT; it++) + for (int it = 0; it < VPT / 4; it++) { - const float tmp = gamma_local[it] * (in_local_dq[it] - mu) * rsigma + beta_local[it]; - in_local[it] = quantize(tmp, qScale); + const float tmp0 = gamma_local[it*4+0] * (in_local_dq[it*4+0] - mu) * rsigma + beta_local[it*4+0]; + const float tmp1 = gamma_local[it*4+1] * (in_local_dq[it*4+1] - mu) * rsigma + beta_local[it*4+1]; + const float tmp2 = gamma_local[it*4+2] * (in_local_dq[it*4+2] - mu) * rsigma + beta_local[it*4+2]; + const float tmp3 = gamma_local[it*4+3] * (in_local_dq[it*4+3] - mu) * rsigma + beta_local[it*4+3]; + out_local[it] = float4_to_char4(tmp0 * qScale, tmp1 * qScale, tmp2 * qScale, tmp3 * qScale); } - copy(in_local, &output[idx]); + copy(out_local, &output[idx]); + } -void launch_small(cudaStream_t stream, const int ld, const int total, const int8_t* input, const int8_t* skip, - const half* beta, const half* gamma, int8_t* output, const float dqScaleIn, const float dqScaleSkip, - const float qScale) +int launch_small_hface(cudaStream_t stream, const int32_t ld, const int32_t total, const int8_t* input, + const int8_t* skip, const half* beta, const half* gamma, int8_t* output, const float dqScaleIn, + const float dqScaleSkip, const float qScale) { - const int gridSize = total; + const int32_t gridSize = total; // we align reads with the number of parameters, i.e. 8-wide instead of 16 - constexpr int VPT = 16 / sizeof(half); // 8 + constexpr int32_t VPT = 16 / sizeof(half); // 8 if (ld == 768) { - constexpr int TPB = 768 / VPT; - skiplnDQQ_vec + constexpr int32_t TPB = 768 / VPT; + skiplnDQQ_vec3 <<>>(ld, input, skip, output, beta, gamma, dqScaleIn, dqScaleSkip, qScale, total); } else if (ld == 1024) { - constexpr int TPB = 1024 / VPT; // 128 - skiplnDQQ_vec + constexpr int32_t TPB = 1024 / VPT; // 128 + skiplnDQQ_vec3 <<>>(ld, input, skip, output, beta, gamma, dqScaleIn, dqScaleSkip, qScale, total); } else { std::cout << "SkipLayerNormDQQ - FATAL: unsupported hidden layer size: " << ld << std::endl; - exit(0); + return STATUS_FAILURE; } - CHECK(cudaPeekAtLastError()); + return cudaPeekAtLastError(); } } // namespace bert + diff --git a/plugin/skipLayerNormPlugin/skipLayerNormInt8InterleavedKernelMTron.cu b/plugin/skipLayerNormPlugin/skipLayerNormInt8InterleavedKernelMTron.cu new file mode 100644 index 00000000..3189fe5e --- /dev/null +++ b/plugin/skipLayerNormPlugin/skipLayerNormInt8InterleavedKernelMTron.cu @@ -0,0 +1,383 @@ + +/* + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * + * 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 "bertCommon.h" +#include "common.cuh" +#include +#include +#include +#include +#include + +using namespace nvinfer1; + +namespace bert +{ + +inline __device__ void res_add( + float (&hdata)[4], const uint32_t idata, const uint32_t ires, const float dqData, const float dqRes) +{ + char4 ires4 = reinterpret_cast(ires); + char4 idata4 = reinterpret_cast(idata); + hdata[0] = float(idata4.x) * dqData + float(ires4.x) * dqRes; + hdata[1] = float(idata4.y) * dqData + float(ires4.y) * dqRes; + hdata[2] = float(idata4.z) * dqData + float(ires4.z) * dqRes; + hdata[3] = float(idata4.w) * dqData + float(ires4.w) * dqRes; +} + +template +__global__ void skipln_vec32_mtron(const int8_t* input, const int8_t* skip, int8_t* output, int8_t* preln, + const half* beta, const half* gamma, const float dqScaleIn, const float dqScaleSkip, const float qScale, + const float qSkipScale, const int32_t total) +{ + + // clang-format off + enum { HEAD_SIZE = 64 }; + enum { BYTES_PER_LDG = 16 }; + enum { THREADS_PER_CTA = WARPS * 32 }; + enum { ROWS_PER_LDG = THREADS_PER_CTA / THREADS_PER_ROW }; + enum { VECS_PER_CTA = THREADS_PER_ROW / 2 }; + enum { PARAM_BYTES = HEADS * HEAD_SIZE * 2 }; + enum { PARAM_LDGS = PARAM_BYTES / (THREADS_PER_CTA * BYTES_PER_LDG) }; + enum { LDGS = HEADS * 2 / ROWS_PER_LDG }; + // clang-format on + static_assert(VECS_PER_CTA == 4, ""); + static_assert(PARAM_LDGS == 1, ""); + static_assert(ROWS_PER_LDG == HEADS, ""); + static_assert(LDGS == 2, ""); + static_assert(LDGS * ROWS_PER_LDG == HEADS * 2, ""); + static_assert(THREADS_PER_CTA * BYTES_PER_LDG == PARAM_BYTES, ""); + static_assert(PARAM_LDGS == 1, ""); + + extern __shared__ char smem_[]; + + // space for CTA-wide reduction + __shared__ half2 smem_red[VECS_PER_CTA][WARPS]; + + constexpr float rld = 1.f / (float(HEADS) * float(HEAD_SIZE)); + const int32_t bidx = blockIdx.x; + const int32_t tidx = threadIdx.x; + const int32_t row = tidx / THREADS_PER_ROW; + const int32_t col = tidx % THREADS_PER_ROW; + const int32_t lane = tidx % 32; + const int32_t warp = tidx / 32; + + const bool is_warp_lead = (lane < THREADS_PER_ROW) && ((lane & 1) == 0); + const bool is_cta_lead = (tidx < THREADS_PER_ROW) && ((tidx & 1) == 0); + + // token position: every two threads load together the 32B at one token + // position + const int32_t pos = col / 2; + + const int32_t pos_offset = bidx * VECS_PER_CTA + pos; // for token positions per block, disabling 2 threads per pos + const bool my_pred = pos_offset < total; + const int32_t row_stride_bytes = total * 32; + + uint4 in_data[LDGS]; + uint4 in_skip[LDGS]; + float hdata[LDGS * 4][4]; + const int32_t gmem_offset = row * row_stride_bytes + (bidx * THREADS_PER_ROW + col) * BYTES_PER_LDG; +#pragma unroll + for (int32_t ii = 0; ii < LDGS; ii++) + { + in_data[ii] = {0, 0, 0, 0}; + in_skip[ii] = {0, 0, 0, 0}; + if (my_pred) + { + ldg(input + gmem_offset + ii * ROWS_PER_LDG * row_stride_bytes, in_data[ii]); + ldg(skip + gmem_offset + ii * ROWS_PER_LDG * row_stride_bytes, in_skip[ii]); + } + } + + uint4* smem_b = reinterpret_cast(&smem_[0]) + tidx; + uint4* smem_g = reinterpret_cast(&smem_[PARAM_BYTES]) + tidx; + + const int8_t* beta_ptr = reinterpret_cast(beta) + tidx * BYTES_PER_LDG; + const int8_t* gamma_ptr = reinterpret_cast(gamma) + tidx * BYTES_PER_LDG; + ldg(beta_ptr, *smem_b); + ldg(gamma_ptr, *smem_g); + + half* b = reinterpret_cast(&smem_[0]); + half* g = reinterpret_cast(&smem_[PARAM_BYTES]); +#pragma unroll + for (int32_t ii = 0; ii < LDGS; ii++) + { + res_add(hdata[ii * 4 + 0], in_data[ii].x, in_skip[ii].x, dqScaleIn, dqScaleSkip); + res_add(hdata[ii * 4 + 1], in_data[ii].y, in_skip[ii].y, dqScaleIn, dqScaleSkip); + res_add(hdata[ii * 4 + 2], in_data[ii].z, in_skip[ii].z, dqScaleIn, dqScaleSkip); + res_add(hdata[ii * 4 + 3], in_data[ii].w, in_skip[ii].w, dqScaleIn, dqScaleSkip); + } + + half2 stats_local = {0, 0}; + +#pragma unroll + for (int32_t ii = 0; ii < LDGS * 4; ii++) + { +#pragma unroll + for (int32_t jj = 0; jj < 4; jj++) + { + const float tmp = hdata[ii][jj] * (rld); + stats_local = stats_local + __floats2half2_rn(tmp, tmp * hdata[ii][jj]); + } + } + stats_local = stats_local + __shfl_xor_sync(uint32_t(-1), stats_local, 1); + __syncwarp(); + + if (VECS_PER_CTA == 1) + { + stats_local = stats_local + __shfl_xor_sync(uint32_t(-1), stats_local, 2); + __syncwarp(); + stats_local = stats_local + __shfl_xor_sync(uint32_t(-1), stats_local, 4); + __syncwarp(); + } + else if (VECS_PER_CTA == 2) + { + stats_local = stats_local + __shfl_xor_sync(uint32_t(-1), stats_local, 4); + __syncwarp(); + } + + stats_local = stats_local + __shfl_xor_sync(uint32_t(-1), stats_local, 8); + __syncwarp(); + stats_local = stats_local + __shfl_xor_sync(uint32_t(-1), stats_local, 16); + __syncwarp(); + + if (is_warp_lead) + { + smem_red[pos][warp] = stats_local; + } + + __syncthreads(); + + if (is_cta_lead) + { + for (int32_t ii = 1; ii < WARPS; ii++) + { + stats_local = stats_local + smem_red[pos][ii]; + } + + float mu = __low2float(stats_local); + float sos = __high2float(stats_local); + float rsigma = rsqrtf(sos - mu * mu); + + smem_red[pos][0] = __floats2half2_rn(mu, rsigma); + } + __syncthreads(); + // load params into smem: 2x Headsx32x2x2B + const float2 statsf = __half22float2(smem_red[pos][0]); + + // Copy skip connection output before Layer Norm +#pragma unroll + for (int32_t ii = 0; ii < LDGS; ii++) + { + in_data[ii].x = pack4(hdata[ii * 4 + 0], qSkipScale); + in_data[ii].y = pack4(hdata[ii * 4 + 1], qSkipScale); + in_data[ii].z = pack4(hdata[ii * 4 + 2], qSkipScale); + in_data[ii].w = pack4(hdata[ii * 4 + 3], qSkipScale); + } + +#pragma unroll + for (int32_t ii = 0; ii < LDGS; ii++) + { + if (my_pred) + { + stg(preln + gmem_offset + ii * ROWS_PER_LDG * row_stride_bytes, in_data[ii]); + } + } + +#pragma unroll + for (int32_t ii = 0; ii < LDGS; ii++) + { +#pragma unroll + for (int32_t jj = 0; jj < 4; jj++) + { +#pragma unroll + for (int32_t kk = 0; kk < 4; kk++) + { + const int32_t param_idx = (ii * ROWS_PER_LDG + row) * 32 + (jj * 4 + kk) + (tidx & 1) * 16; + const float bb = b[param_idx]; + const float gg = g[param_idx]; + hdata[ii * 4 + jj][kk] = gg * statsf.y * (hdata[ii * 4 + jj][kk] - statsf.x) + bb; + } + } + } + +#pragma unroll + for (int32_t ii = 0; ii < LDGS; ii++) + { + in_data[ii].x = pack4(hdata[ii * 4 + 0], qScale); + in_data[ii].y = pack4(hdata[ii * 4 + 1], qScale); + in_data[ii].z = pack4(hdata[ii * 4 + 2], qScale); + in_data[ii].w = pack4(hdata[ii * 4 + 3], qScale); + } + +#pragma unroll + for (int32_t ii = 0; ii < LDGS; ii++) + { + if (my_pred) + { + stg(output + gmem_offset + ii * ROWS_PER_LDG * row_stride_bytes, in_data[ii]); + } + } + // store +} + +int32_t launch_large_mtron(cudaStream_t stream, const int32_t ld, const int32_t total, const int8_t* input, + const int8_t* skip, const half* beta, const half* gamma, int8_t* output, int8_t* preln, const float dqScaleIn, + const float dqScaleSkip, const float qScale, const float qSkipScale) +{ + if (ld == 1024) + { + constexpr int32_t WARPS = 4; + constexpr int32_t THREADS_PER_ROW = 8; + constexpr int32_t HEADS = 16; + constexpr int32_t PARAM_BYTES = HEADS * 64 * 2 * sizeof(half); + constexpr int32_t VECS_PER_CTA = THREADS_PER_ROW / 2; + const int32_t blocks = (total + VECS_PER_CTA - 1) / VECS_PER_CTA; + + skipln_vec32_mtron<<>>( + input, skip, output, preln, beta, gamma, dqScaleIn, dqScaleSkip, qScale, qSkipScale, total); + } + else if (ld == 768) + { + constexpr int32_t WARPS = 3; + constexpr int32_t THREADS_PER_ROW = 8; + constexpr int32_t HEADS = 12; + constexpr int32_t PARAM_BYTES = HEADS * 64 * 2 * sizeof(half); + constexpr int32_t VECS_PER_CTA = THREADS_PER_ROW / 2; + const int32_t blocks = (total + VECS_PER_CTA - 1) / VECS_PER_CTA; + + skipln_vec32_mtron<<>>( + input, skip, output, preln, beta, gamma, dqScaleIn, dqScaleSkip, qScale, qSkipScale, total); + } + else + { + return STATUS_FAILURE; + } + + return cudaPeekAtLastError(); +} + +// naive kernel that only changes the addressing seems to be faster for small problem sizes +template +__global__ void skiplnDQQ_vec4(const int32_t ld, const int8_t* input, const int8_t* skip, int8_t* output, int8_t* preln, + const half* beta, const half* gamma, const float dqScaleIn, const float dqScaleSkip, const float qScale, + const float qSkipScale, const int32_t total) +{ + const int32_t hinner = threadIdx.x % 4; + const int32_t houter = threadIdx.x / 4; + + const int32_t tidx = threadIdx.x; + const int32_t bidx = blockIdx.x; + const int32_t idx = houter * total * 32 + bidx * 32 + hinner * VPT; + // 4 * 1024 * 4 * 2 Bytes = 16KB per block + int8_t in_local[VPT]; + int8_t skip_local[VPT]; + + half in_local_dq[VPT]; // dequantized input + skip + half beta_local[VPT]; + half gamma_local[VPT]; + + // load input tensors + copy(&input[idx], in_local); + copy(&skip[idx], skip_local); + + // load parameters + copy(&beta[tidx * VPT], beta_local); + copy(&gamma[tidx * VPT], gamma_local); + + half2 stats_local = __floats2half2_rn(0.f, 0.f); // accumulator + + const half rld = half(1.f) / half(ld); +#pragma unroll + for (int32_t it = 0; it < VPT; it++) + { + // DQ input and skip + const float tmp_in = in_local[it]; + const float tmp_skip = skip_local[it]; + in_local_dq[it] = dqScaleIn * tmp_in + dqScaleSkip * tmp_skip; + + const half tmp = rld * in_local_dq[it]; + const half2 tmp2 = __halves2half2(tmp, tmp * in_local_dq[it]); + stats_local = stats_local + tmp2; + } + + using BlockReduce = cub::BlockReduce; + __shared__ typename BlockReduce::TempStorage temp_storage; + __shared__ half mu; // mean + __shared__ half rsigma; // 1 / std.dev. + + const half2 sum2 = BlockReduce(temp_storage).Reduce(stats_local, cub::Sum()); + + // Copy skip connection output before Layer Norm +#pragma unroll + for (int32_t it = 0; it < VPT; it++) + { + in_local[it] = quantize(in_local_dq[it], qSkipScale); + } + copy(in_local, &preln[idx]); + + if (tidx == 0) + { + mu = __low2half(sum2); + rsigma = rsqrtf(__high2half(sum2) - mu * mu); + } + + __syncthreads(); + + static_assert(VPT % 4 == 0, ""); + uint32_t out_local[VPT/4]; +#pragma unroll + for (int it = 0; it < VPT / 4; it++) + { + const float tmp0 = gamma_local[it*4+0] * (in_local_dq[it*4+0] - mu) * rsigma + beta_local[it*4+0]; + const float tmp1 = gamma_local[it*4+1] * (in_local_dq[it*4+1] - mu) * rsigma + beta_local[it*4+1]; + const float tmp2 = gamma_local[it*4+2] * (in_local_dq[it*4+2] - mu) * rsigma + beta_local[it*4+2]; + const float tmp3 = gamma_local[it*4+3] * (in_local_dq[it*4+3] - mu) * rsigma + beta_local[it*4+3]; + out_local[it] = float4_to_char4(tmp0 * qScale, tmp1 * qScale, tmp2 * qScale, tmp3 * qScale); + } + + copy(out_local, &output[idx]); +} + +int32_t launch_small_mtron(cudaStream_t stream, const int32_t ld, const int total, const int8_t* input, + const int8_t* skip, const half* beta, const half* gamma, int8_t* output, int8_t* preln, const float dqScaleIn, + const float dqScaleSkip, const float qScale, const float qSkipScale) +{ + const int32_t gridSize = total; + // we align reads with the number of parameters, i.e. 8-wide instead of 16 + constexpr int32_t VPT = 16 / sizeof(half); // 8 + if (ld == 768) + { + constexpr int32_t TPB = 768 / VPT; + skiplnDQQ_vec4<<>>( + ld, input, skip, output, preln, beta, gamma, dqScaleIn, dqScaleSkip, qScale, qSkipScale, total); + } + else if (ld == 1024) + { + constexpr int32_t TPB = 1024 / VPT; // 128 + skiplnDQQ_vec4<<>>( + ld, input, skip, output, preln, beta, gamma, dqScaleIn, dqScaleSkip, qScale, qSkipScale, total); + } + else + { + std::cout << "SkipLayerNormDQQ - FATAL: unsupported hidden layer size: " << ld << std::endl; + return STATUS_FAILURE; + } + return cudaPeekAtLastError(); +} + +} // namespace bert diff --git a/plugin/skipLayerNormPlugin/skipLayerNormInt8InterleavedPlugin.cpp b/plugin/skipLayerNormPlugin/skipLayerNormInt8InterleavedPlugin.cpp index f9962caf..694690e3 100644 --- a/plugin/skipLayerNormPlugin/skipLayerNormInt8InterleavedPlugin.cpp +++ b/plugin/skipLayerNormPlugin/skipLayerNormInt8InterleavedPlugin.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "skipLayerNormInt8InterleavedPlugin.h" +#include #include "NvInfer.h" #include "serialize.hpp" -#include +#include "skipLayerNormInt8InterleavedPlugin.h" #include #include @@ -27,26 +27,36 @@ using namespace nvinfer1; namespace bert { -void launch_small(cudaStream_t stream, const int ld, const int total, const int8_t* input, const int8_t* skip, - const half* beta, const half* gamma, int8_t* output, const float dqScaleIn, const float dqScaleSkip, - const float qScale); +int32_t launch_small_hface(cudaStream_t stream, const int32_t ld, const int32_t total, const int8_t* input, + const int8_t* skip, const half* beta, const half* gamma, int8_t* output, const float dqScaleIn, + const float dqScaleSkip, const float qScale); -void launch_large(cudaStream_t stream, const int ld, const int total, const int8_t* input, const int8_t* skip, - const half* beta, const half* gamma, int8_t* output, const float dqScaleIn, const float dqScaleSkip, - const float qScale); +int32_t launch_large_hface(cudaStream_t stream, const int32_t ld, const int32_t total, const int8_t* input, + const int8_t* skip, const half* beta, const half* gamma, int8_t* output, const float dqScaleIn, + const float dqScaleSkip, const float qScale); + +int32_t launch_small_mtron(cudaStream_t stream, const int32_t ld, const int32_t total, const int8_t* input, + const int8_t* skip, const half* beta, const half* gamma, int8_t* output, int8_t* preln, const float dqScaleIn, + const float dqScaleSkip, const float qScale, const float qSkipScale); + +int32_t launch_large_mtron(cudaStream_t stream, const int32_t ld, const int32_t total, const int8_t* input, + const int8_t* skip, const half* beta, const half* gamma, int8_t* output, int8_t* preln, const float dqScaleIn, + const float dqScaleSkip, const float qScale, const float qSkipScale); // Clip plugin specific constants namespace { -static const char* SKIP_LAYER_NORM_INTERLEAVED_VERSION{"3"}; -static const char* SKIP_LAYER_NORM_INTERLEAVED_NAME{"CustomSkipLayerNormPluginDynamic"}; +const char* SKIP_LAYER_NORM_INTERLEAVED_VERSION_HFACE{"3"}; +const char* SKIP_LAYER_NORM_INTERLEAVED_VERSION_MTRON{"4"}; +const char* SKIP_LAYER_NORM_INTERLEAVED_NAME{"CustomSkipLayerNormPluginDynamic"}; } // namespace // Static class fields initialization -PluginFieldCollection SkipLayerNormInterleavedPluginCreator::mFC{}; -std::vector SkipLayerNormInterleavedPluginCreator::mPluginAttributes; +PluginFieldCollection SkipLayerNormInterleavedPluginBaseCreator::mFC{}; +std::vector SkipLayerNormInterleavedPluginBaseCreator::mPluginAttributes; -REGISTER_TENSORRT_PLUGIN(SkipLayerNormInterleavedPluginCreator); +REGISTER_TENSORRT_PLUGIN(SkipLayerNormInterleavedPluginHFaceCreator); +REGISTER_TENSORRT_PLUGIN(SkipLayerNormInterleavedPluginMTronCreator); constexpr auto param_type = DataType::kHALF; @@ -60,7 +70,7 @@ static inline DataType getParamWordType(DataType cfgType) return cfgType; } -SkipLayerNormInterleavedPlugin::SkipLayerNormInterleavedPlugin( +SkipLayerNormInterleavedPluginBase::SkipLayerNormInterleavedPluginBase( const std::string name, const Weights& beta, const Weights& gamma) : mLayerName(name) , mGammaDev(nullptr) @@ -78,14 +88,25 @@ SkipLayerNormInterleavedPlugin::SkipLayerNormInterleavedPlugin( mGamma.convertAndCopy(gamma, param_type); } -SkipLayerNormInterleavedPlugin::SkipLayerNormInterleavedPlugin(const std::string name, const void* data, size_t length) +SkipLayerNormInterleavedPluginHFace::SkipLayerNormInterleavedPluginHFace( + const std::string name, const Weights& beta, const Weights& gamma) + : SkipLayerNormInterleavedPluginBase(name, beta, gamma) +{ +} + +SkipLayerNormInterleavedPluginMTron::SkipLayerNormInterleavedPluginMTron( + const std::string name, const Weights& beta, const Weights& gamma) + : SkipLayerNormInterleavedPluginBase(name, beta, gamma) +{ +} + +SkipLayerNormInterleavedPluginBase::SkipLayerNormInterleavedPluginBase( + const std::string name, const void* data, size_t length) : mLayerName(name) , mGammaDev(nullptr) , mBetaDev(nullptr) , mParamsOnDevice(false) { - gLogVerbose << "SkipLayerNormInterleavedPlugin deserialize\n"; - // Deserialize in the same order as serialization deserialize_value(&data, &length, &mLd); @@ -96,41 +117,65 @@ SkipLayerNormInterleavedPlugin::SkipLayerNormInterleavedPlugin(const std::string mGamma.convertAndCopy(d, mLd, param_type); } -// IPluginV2DynamicExt Methods -IPluginV2DynamicExt* SkipLayerNormInterleavedPlugin::clone() const +SkipLayerNormInterleavedPluginHFace::SkipLayerNormInterleavedPluginHFace( + const std::string name, const void* data, size_t length) + : SkipLayerNormInterleavedPluginBase(name, data, length) { - gLogVerbose << "SkipLayerNormInterleavedPlugin clone\n"; + gLogVerbose << "SkipLayerNormInterleavedPluginHFace deserialize\n"; +} - auto p = new SkipLayerNormInterleavedPlugin(mLayerName, mBeta, mGamma); +SkipLayerNormInterleavedPluginMTron::SkipLayerNormInterleavedPluginMTron( + const std::string name, const void* data, size_t length) + : SkipLayerNormInterleavedPluginBase(name, data, length) +{ + gLogVerbose << "SkipLayerNormInterleavedPluginMTron deserialize\n"; +} + +// IPluginV2DynamicExt Methods +IPluginV2DynamicExt* SkipLayerNormInterleavedPluginHFace::clone() const noexcept +{ + gLogVerbose << "SkipLayerNormInterleavedPluginHFace clone\n"; + + auto* p = new SkipLayerNormInterleavedPluginHFace(mLayerName, mBeta, mGamma); p->initialize(); p->setPluginNamespace(mNamespace.c_str()); return p; } -DimsExprs SkipLayerNormInterleavedPlugin::getOutputDimensions( - int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) +IPluginV2DynamicExt* SkipLayerNormInterleavedPluginMTron::clone() const noexcept +{ + gLogVerbose << "SkipLayerNormInterleavedPluginMTron clone\n"; + + auto* p = new SkipLayerNormInterleavedPluginMTron(mLayerName, mBeta, mGamma); + p->initialize(); + p->setPluginNamespace(mNamespace.c_str()); + return p; +} + +DimsExprs SkipLayerNormInterleavedPluginBase::getOutputDimensions( + int32_t outputIndex, const DimsExprs* inputs, int32_t nbInputs, IExprBuilder& exprBuilder) noexcept { ASSERT(nbInputs == 2); - ASSERT(outputIndex == 0); + ASSERT(outputIndex >= 0 && outputIndex < getNbOutputs()); ASSERT(inputs[0].nbDims == inputs[1].nbDims); return inputs[0]; } -bool SkipLayerNormInterleavedPlugin::supportsFormatCombination( - int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) +bool SkipLayerNormInterleavedPluginBase::supportsFormatCombination( + int32_t pos, const PluginTensorDesc* inOut, int32_t nbInputs, int32_t nbOutputs) noexcept { ASSERT(nbInputs == 2); - ASSERT(nbOutputs == 1); + ASSERT(nbOutputs == getNbOutputs()); const PluginTensorDesc& desc = inOut[pos]; return desc.type == DataType::kINT8 && desc.format == TensorFormat::kCHW32; } -void SkipLayerNormInterleavedPlugin::configurePlugin( - const DynamicPluginTensorDesc* inputs, int nbInputs, const DynamicPluginTensorDesc* outputs, int nbOutputs) +void SkipLayerNormInterleavedPluginBase::configurePlugin(const DynamicPluginTensorDesc* inputs, int32_t nbInputs, + const DynamicPluginTensorDesc* outputs, int32_t nbOutputs) noexcept { // Validate input arguments - ASSERT(nbOutputs == 1); + ASSERT(nbOutputs == getNbOutputs()); ASSERT(nbInputs == 2); ASSERT(DataType::kINT8 == inputs[0].desc.type); ASSERT(DataType::kINT8 == inputs[1].desc.type); @@ -152,19 +197,14 @@ void SkipLayerNormInterleavedPlugin::configurePlugin( } } -size_t SkipLayerNormInterleavedPlugin::getWorkspaceSize( - const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const +size_t SkipLayerNormInterleavedPluginBase::getWorkspaceSize( + const PluginTensorDesc* inputs, int32_t nbInputs, const PluginTensorDesc* outputs, int32_t nbOutputs) const noexcept { return 0; } -int SkipLayerNormInterleavedPlugin::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) +void checkDescs(const PluginTensorDesc& iDesc, const PluginTensorDesc& sDesc, const PluginTensorDesc& oDesc) { - // Input shape: 1x(hxd)xtotalx1 - const auto iDesc = inputDesc[0]; - const auto sDesc = inputDesc[1]; - const auto oDesc = outputDesc[0]; ASSERT(iDesc.dims.nbDims == 4); ASSERT(iDesc.dims.nbDims == sDesc.dims.nbDims); ASSERT(std::equal(iDesc.dims.d, iDesc.dims.d + iDesc.dims.nbDims, sDesc.dims.d)); @@ -177,11 +217,22 @@ int SkipLayerNormInterleavedPlugin::enqueue(const PluginTensorDesc* inputDesc, c ASSERT(iDesc.format == oDesc.format); ASSERT(iDesc.type == sDesc.type); ASSERT(iDesc.type == oDesc.type); - const int ld = iDesc.dims.d[1]; - const int total = iDesc.dims.d[2]; +} + +int32_t SkipLayerNormInterleavedPluginHFace::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept +{ + // Input shape: 1x(hxd)xtotalx1 + const auto iDesc = inputDesc[0]; + const auto sDesc = inputDesc[1]; + const auto oDesc = outputDesc[0]; + checkDescs(iDesc, sDesc, oDesc); + + const int32_t ld = iDesc.dims.d[1]; + const int32_t total = iDesc.dims.d[2]; const float dqScaleIn = iDesc.scale; const float dqScaleSkip = sDesc.scale; - const float qScale = 1.f / oDesc.scale; + const float qScale = 1.F / oDesc.scale; const int8_t* input = static_cast(inputs[0]); const int8_t* skip = static_cast(inputs[1]); int8_t* output = static_cast(outputs[0]); @@ -190,56 +241,115 @@ int SkipLayerNormInterleavedPlugin::enqueue(const PluginTensorDesc* inputDesc, c if (total < 4096) { - launch_small(stream, ld, total, input, skip, beta, gamma, output, dqScaleIn, dqScaleSkip, qScale); + return launch_small_hface(stream, ld, total, input, skip, beta, gamma, output, dqScaleIn, dqScaleSkip, qScale); } else { - launch_large(stream, ld, total, input, skip, beta, gamma, output, dqScaleIn, dqScaleSkip, qScale); + return launch_large_hface(stream, ld, total, input, skip, beta, gamma, output, dqScaleIn, dqScaleSkip, qScale); + } +} + +int32_t SkipLayerNormInterleavedPluginMTron::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept +{ + // Input shape: 1x(hxd)xtotalx1 + const auto iDesc = inputDesc[0]; + const auto sDesc = inputDesc[1]; + const auto oDesc = outputDesc[0]; + const auto pDesc = outputDesc[1]; + checkDescs(iDesc, sDesc, oDesc); + ASSERT(std::equal(iDesc.dims.d, iDesc.dims.d + iDesc.dims.nbDims, pDesc.dims.d)); + + const int32_t ld = iDesc.dims.d[1]; + const int32_t total = iDesc.dims.d[2]; + const float dqScaleIn = iDesc.scale; + const float dqScaleSkip = sDesc.scale; + const float qScale = 1.F / oDesc.scale; + const float qSkipScale = 1.F / pDesc.scale; + const int8_t* input = static_cast(inputs[0]); + const int8_t* skip = static_cast(inputs[1]); + int8_t* output = static_cast(outputs[0]); + int8_t* preln = static_cast(outputs[1]); + const half* gamma = static_cast(mGammaDev.get()); + const half* beta = static_cast(mBetaDev.get()); + + if (total < 4096) + { + return launch_small_mtron( + stream, ld, total, input, skip, beta, gamma, output, preln, dqScaleIn, dqScaleSkip, qScale, qSkipScale); + } + else + { + return launch_large_mtron( + stream, ld, total, input, skip, beta, gamma, output, preln, dqScaleIn, dqScaleSkip, qScale, qSkipScale); } return 0; } // IPluginV2Ext Methods -DataType SkipLayerNormInterleavedPlugin::getOutputDataType(int index, const DataType* inputTypes, int nbInputs) const +DataType SkipLayerNormInterleavedPluginBase::getOutputDataType( + int32_t index, const DataType* inputTypes, int32_t nbInputs) const noexcept { - ASSERT(index == 0); + ASSERT(index >= 0 && index < getNbOutputs()); ASSERT(nbInputs == 2); return inputTypes[0]; } // IPluginV2 Methods -const char* SkipLayerNormInterleavedPlugin::getPluginType() const +const char* SkipLayerNormInterleavedPluginBase::getPluginType() const noexcept { return SKIP_LAYER_NORM_INTERLEAVED_NAME; } -const char* SkipLayerNormInterleavedPlugin::getPluginVersion() const +const char* SkipLayerNormInterleavedPluginHFace::getPluginVersion() const noexcept { - return SKIP_LAYER_NORM_INTERLEAVED_VERSION; + return SKIP_LAYER_NORM_INTERLEAVED_VERSION_HFACE; } -int SkipLayerNormInterleavedPlugin::getNbOutputs() const +const char* SkipLayerNormInterleavedPluginMTron::getPluginVersion() const noexcept +{ + return SKIP_LAYER_NORM_INTERLEAVED_VERSION_MTRON; +} + +int32_t SkipLayerNormInterleavedPluginHFace::getNbOutputs() const noexcept { return 1; } -int SkipLayerNormInterleavedPlugin::initialize() + +int32_t SkipLayerNormInterleavedPluginMTron::getNbOutputs() const noexcept { - gLogVerbose << "SkipLayerNormInterleavedPlugin initialize\n"; + return 2; +} + +int32_t SkipLayerNormInterleavedPluginHFace::initialize() noexcept +{ + gLogVerbose << "SkipLayerNormInterleavedPluginHFace initialize\n"; return 0; } -void SkipLayerNormInterleavedPlugin::terminate() +int32_t SkipLayerNormInterleavedPluginMTron::initialize() noexcept { - gLogVerbose << "SkipLayerNormInterleavedPlugin terminate\n"; + gLogVerbose << "SkipLayerNormInterleavedPluginMTron initialize\n"; + return 0; } -size_t SkipLayerNormInterleavedPlugin::getSerializationSize() const +void SkipLayerNormInterleavedPluginHFace::terminate() noexcept +{ + gLogVerbose << "SkipLayerNormInterleavedPluginHFace terminate\n"; +} + +void SkipLayerNormInterleavedPluginMTron::terminate() noexcept +{ + gLogVerbose << "SkipLayerNormInterleavedPluginMTron terminate\n"; +} + +size_t SkipLayerNormInterleavedPluginBase::getSerializationSize() const noexcept { return 2 * mParamWordsize * mLd + sizeof(mLd); } -void SkipLayerNormInterleavedPlugin::serialize(void* buffer) const +void SkipLayerNormInterleavedPluginBase::serialize(void* buffer) const noexcept { serialize_value(&buffer, mLd); @@ -248,56 +358,77 @@ void SkipLayerNormInterleavedPlugin::serialize(void* buffer) const serFromDev(d, static_cast(mGammaDev.get()), mLd * mParamWordsize); } -void SkipLayerNormInterleavedPlugin::destroy() +void SkipLayerNormInterleavedPluginBase::destroy() noexcept { - gLogVerbose << "SkipLayerNormInterleavedPlugin destroy\n"; // This gets called when the network containing plugin is destroyed - mGammaDev.release(); - mBetaDev.release(); + mGammaDev.reset(nullptr); + mBetaDev.reset(nullptr); delete this; } -void SkipLayerNormInterleavedPlugin::setPluginNamespace(const char* libNamespace) +void SkipLayerNormInterleavedPluginHFace::destroy() noexcept +{ + gLogVerbose << "SkipLayerNormInterleavedPluginHFace destroy\n"; + SkipLayerNormInterleavedPluginBase::destroy(); +} + +void SkipLayerNormInterleavedPluginMTron::destroy() noexcept +{ + gLogVerbose << "SkipLayerNormInterleavedPluginMTron destroy\n"; + SkipLayerNormInterleavedPluginBase::destroy(); +} + +void SkipLayerNormInterleavedPluginBase::setPluginNamespace(const char* libNamespace) noexcept { mNamespace = libNamespace; } -const char* SkipLayerNormInterleavedPlugin::getPluginNamespace() const +const char* SkipLayerNormInterleavedPluginBase::getPluginNamespace() const noexcept { return mNamespace.c_str(); } ///////////////////////////////////////////////////////// -SkipLayerNormInterleavedPluginCreator::SkipLayerNormInterleavedPluginCreator() +SkipLayerNormInterleavedPluginBaseCreator::SkipLayerNormInterleavedPluginBaseCreator() { mFC.nbFields = mPluginAttributes.size(); mFC.fields = mPluginAttributes.data(); } -const char* SkipLayerNormInterleavedPluginCreator::getPluginName() const +SkipLayerNormInterleavedPluginHFaceCreator::SkipLayerNormInterleavedPluginHFaceCreator() + : SkipLayerNormInterleavedPluginBaseCreator() +{ +} + +SkipLayerNormInterleavedPluginMTronCreator::SkipLayerNormInterleavedPluginMTronCreator() + : SkipLayerNormInterleavedPluginBaseCreator() +{ +} + +const char* SkipLayerNormInterleavedPluginBaseCreator::getPluginName() const noexcept { return SKIP_LAYER_NORM_INTERLEAVED_NAME; } -const char* SkipLayerNormInterleavedPluginCreator::getPluginVersion() const +const char* SkipLayerNormInterleavedPluginHFaceCreator::getPluginVersion() const noexcept { - return SKIP_LAYER_NORM_INTERLEAVED_VERSION; + return SKIP_LAYER_NORM_INTERLEAVED_VERSION_HFACE; } -const PluginFieldCollection* SkipLayerNormInterleavedPluginCreator::getFieldNames() +const char* SkipLayerNormInterleavedPluginMTronCreator::getPluginVersion() const noexcept +{ + return SKIP_LAYER_NORM_INTERLEAVED_VERSION_MTRON; +} + +const PluginFieldCollection* SkipLayerNormInterleavedPluginBaseCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2* SkipLayerNormInterleavedPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +void buildBetaAndGamma(const PluginFieldCollection* fc, Weights& beta, Weights& gamma) { - gLogVerbose << "SkipLayerNormInterleavedPluginCreator createPlugin\n"; - - Weights beta{DataType::kFLOAT, nullptr, 0}; - Weights gamma{DataType::kFLOAT, nullptr, 0}; - - for (int i = 0; i < fc->nbFields; i++) + for (int32_t i = 0; i < fc->nbFields; i++) { std::string field_name(fc->fields[i].name); @@ -327,24 +458,88 @@ IPluginV2* SkipLayerNormInterleavedPluginCreator::createPlugin(const char* name, { gLogError << "SkipLayerNorm: invalid gamma" << std::endl; } - - return new SkipLayerNormInterleavedPlugin(name, beta, gamma); } -IPluginV2* SkipLayerNormInterleavedPluginCreator::deserializePlugin( - const char* name, const void* serialData, size_t serialLength) +IPluginV2* SkipLayerNormInterleavedPluginHFaceCreator::createPlugin( + const char* name, const PluginFieldCollection* fc) noexcept +{ + try + { + gLogVerbose << "SkipLayerNormInterleavedPluginHFaceCreator createPlugin\n"; + + Weights beta{DataType::kFLOAT, nullptr, 0}; + Weights gamma{DataType::kFLOAT, nullptr, 0}; + buildBetaAndGamma(fc, beta, gamma); + + return new SkipLayerNormInterleavedPluginHFace(name, beta, gamma); + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; +} + +IPluginV2* SkipLayerNormInterleavedPluginMTronCreator::createPlugin( + const char* name, const PluginFieldCollection* fc) noexcept +{ + try + { + gLogVerbose << "SkipLayerNormInterleavedPluginMTronCreator createPlugin\n"; + + Weights beta{DataType::kFLOAT, nullptr, 0}; + Weights gamma{DataType::kFLOAT, nullptr, 0}; + buildBetaAndGamma(fc, beta, gamma); + + return new SkipLayerNormInterleavedPluginMTron(name, beta, gamma); + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; +} + +IPluginV2* SkipLayerNormInterleavedPluginHFaceCreator::deserializePlugin( + const char* name, const void* serialData, size_t serialLength) noexcept { // This object will be deleted when the network is destroyed, which will // call SkipLayerNormInterleavedPlugin::destroy() - return new SkipLayerNormInterleavedPlugin(name, serialData, serialLength); + try + { + gLogVerbose << "SkipLayerNormInterleavedPluginHFaceCreator deserializePlugin\n"; + return new SkipLayerNormInterleavedPluginHFace(name, serialData, serialLength); + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } -void SkipLayerNormInterleavedPluginCreator::setPluginNamespace(const char* libNamespace) +IPluginV2* SkipLayerNormInterleavedPluginMTronCreator::deserializePlugin( + const char* name, const void* serialData, size_t serialLength) noexcept +{ + // This object will be deleted when the network is destroyed, which will + // call SkipLayerNormInterleavedPlugin::destroy() + try + { + gLogVerbose << "SkipLayerNormInterleavedPluginMTronCreator deserializePlugin\n"; + return new SkipLayerNormInterleavedPluginMTron(name, serialData, serialLength); + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; +} + +void SkipLayerNormInterleavedPluginBaseCreator::setPluginNamespace(const char* libNamespace) noexcept { mNamespace = libNamespace; } -const char* SkipLayerNormInterleavedPluginCreator::getPluginNamespace() const +const char* SkipLayerNormInterleavedPluginBaseCreator::getPluginNamespace() const noexcept { return mNamespace.c_str(); } diff --git a/plugin/skipLayerNormPlugin/skipLayerNormInt8InterleavedPlugin.h b/plugin/skipLayerNormPlugin/skipLayerNormInt8InterleavedPlugin.h index 72ad11fe..8d2179ce 100644 --- a/plugin/skipLayerNormPlugin/skipLayerNormInt8InterleavedPlugin.h +++ b/plugin/skipLayerNormPlugin/skipLayerNormInt8InterleavedPlugin.h @@ -16,8 +16,8 @@ #ifndef TRT_SKIP_LAYER_NORM_INTERLEAVED_PLUGIN_H #define TRT_SKIP_LAYER_NORM_INTERLEAVED_PLUGIN_H -#include "NvInferPlugin.h" #include +#include "NvInferPlugin.h" #include "bertCommon.h" #include @@ -27,47 +27,41 @@ namespace bert { -class SkipLayerNormInterleavedPlugin : public nvinfer1::IPluginV2DynamicExt +class SkipLayerNormInterleavedPluginBase : public nvinfer1::IPluginV2DynamicExt { public: - SkipLayerNormInterleavedPlugin( + SkipLayerNormInterleavedPluginBase( const std::string name, const nvinfer1::Weights& beta, const nvinfer1::Weights& gamma); - SkipLayerNormInterleavedPlugin(const std::string name, const void* data, size_t length); + SkipLayerNormInterleavedPluginBase(const std::string name, const void* data, size_t length); // It doesn't make sense to make SkipLayerNormInterleavedPlugin without arguments, so we // delete default constructor. - SkipLayerNormInterleavedPlugin() = delete; + SkipLayerNormInterleavedPluginBase() = delete; // IPluginV2DynamicExt Methods - nvinfer1::IPluginV2DynamicExt* clone() const override; - nvinfer1::DimsExprs getOutputDimensions( - int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) override; + nvinfer1::DimsExprs getOutputDimensions(int32_t outputIndex, const nvinfer1::DimsExprs* inputs, int32_t nbInputs, + nvinfer1::IExprBuilder& exprBuilder) noexcept override; bool supportsFormatCombination( - int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) override; - void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, - const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) override; - size_t getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int nbInputs, - const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const override; - int enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) override; + int32_t pos, const nvinfer1::PluginTensorDesc* inOut, int32_t nbInputs, int32_t nbOutputs) noexcept override; + void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int32_t nbInputs, + const nvinfer1::DynamicPluginTensorDesc* out, int32_t nbOutputs) noexcept override; + size_t getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int32_t nbInputs, + const nvinfer1::PluginTensorDesc* outputs, int32_t nbOutputs) const noexcept override; // IPluginV2Ext Methods - nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + nvinfer1::DataType getOutputDataType(int32_t index, const nvinfer1::DataType* inputTypes, int32_t nbInputs) const + noexcept override; // IPluginV2 Methods - const char* getPluginType() const override; - const char* getPluginVersion() const override; - int getNbOutputs() const override; - int initialize() override; - void terminate() override; - size_t getSerializationSize() const override; - void serialize(void* buffer) const override; - void destroy() override; - void setPluginNamespace(const char* pluginNamespace) override; - const char* getPluginNamespace() const override; + const char* getPluginType() const noexcept override; + size_t getSerializationSize() const noexcept override; + void serialize(void* buffer) const noexcept override; + void destroy() noexcept override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; + const char* getPluginNamespace() const noexcept override; -private: +protected: const std::string mLayerName; std::string mNamespace; @@ -79,41 +73,100 @@ private: size_t mParamWordsize; bool mParamsOnDevice; - -protected: - // To prevent compiler warnings. - using nvinfer1::IPluginV2DynamicExt::canBroadcastInputAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::configurePlugin; - using nvinfer1::IPluginV2DynamicExt::enqueue; - using nvinfer1::IPluginV2DynamicExt::getOutputDimensions; - using nvinfer1::IPluginV2DynamicExt::getWorkspaceSize; - using nvinfer1::IPluginV2DynamicExt::isOutputBroadcastAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::supportsFormat; }; -class SkipLayerNormInterleavedPluginCreator : public nvinfer1::IPluginCreator +class SkipLayerNormInterleavedPluginHFace : public SkipLayerNormInterleavedPluginBase { public: - SkipLayerNormInterleavedPluginCreator(); + SkipLayerNormInterleavedPluginHFace( + const std::string name, const nvinfer1::Weights& beta, const nvinfer1::Weights& gamma); - const char* getPluginName() const override; + SkipLayerNormInterleavedPluginHFace(const std::string name, const void* data, size_t length); - const char* getPluginVersion() const override; + // It doesn't make sense to make SkipLayerNormInterleavedPlugin without arguments, so we + // delete default constructor. + SkipLayerNormInterleavedPluginHFace() = delete; - const nvinfer1::PluginFieldCollection* getFieldNames() override; + // IPluginV2DynamicExt Methods + nvinfer1::IPluginV2DynamicExt* clone() const noexcept override; + int32_t enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; - nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) override; + // IPluginV2 Methods + int32_t initialize() noexcept override; + void terminate() noexcept override; + void destroy() noexcept override; + const char* getPluginVersion() const noexcept override; + int32_t getNbOutputs() const noexcept override; +}; - nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; +class SkipLayerNormInterleavedPluginMTron : public SkipLayerNormInterleavedPluginBase +{ +public: + SkipLayerNormInterleavedPluginMTron( + const std::string name, const nvinfer1::Weights& beta, const nvinfer1::Weights& gamma); - void setPluginNamespace(const char* pluginNamespace) override; + SkipLayerNormInterleavedPluginMTron(const std::string name, const void* data, size_t length); - const char* getPluginNamespace() const override; + // It doesn't make sense to make SkipLayerNormInterleavedPlugin without arguments, so we + // delete default constructor. + SkipLayerNormInterleavedPluginMTron() = delete; + + // IPluginV2DynamicExt Methods + nvinfer1::IPluginV2DynamicExt* clone() const noexcept override; + int32_t enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; + + // IPluginV2 Methods + int32_t initialize() noexcept override; + void terminate() noexcept override; + void destroy() noexcept override; + const char* getPluginVersion() const noexcept override; + int32_t getNbOutputs() const noexcept override; +}; + +class SkipLayerNormInterleavedPluginBaseCreator : public nvinfer1::IPluginCreator +{ +public: + SkipLayerNormInterleavedPluginBaseCreator(); + + const char* getPluginName() const noexcept override; + + const nvinfer1::PluginFieldCollection* getFieldNames() noexcept override; + + void setPluginNamespace(const char* pluginNamespace) noexcept override; + + const char* getPluginNamespace() const noexcept override; private: static nvinfer1::PluginFieldCollection mFC; static std::vector mPluginAttributes; std::string mNamespace; }; + +class SkipLayerNormInterleavedPluginHFaceCreator : public SkipLayerNormInterleavedPluginBaseCreator +{ +public: + SkipLayerNormInterleavedPluginHFaceCreator(); + + const char* getPluginVersion() const noexcept override; + + nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) noexcept override; + nvinfer1::IPluginV2* deserializePlugin( + const char* name, const void* serialData, size_t serialLength) noexcept override; +}; + +class SkipLayerNormInterleavedPluginMTronCreator : public SkipLayerNormInterleavedPluginBaseCreator +{ +public: + SkipLayerNormInterleavedPluginMTronCreator(); + + const char* getPluginVersion() const noexcept override; + + nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) noexcept override; + nvinfer1::IPluginV2* deserializePlugin( + const char* name, const void* serialData, size_t serialLength) noexcept override; +}; + } // namespace bert #endif // TRT_SKIP_LAYER_NORM_INTERLEAVED_PLUGIN_H diff --git a/plugin/skipLayerNormPlugin/skipLayerNormKernel.cu b/plugin/skipLayerNormPlugin/skipLayerNormKernel.cu index e31c8497..0a587db4 100644 --- a/plugin/skipLayerNormPlugin/skipLayerNormKernel.cu +++ b/plugin/skipLayerNormPlugin/skipLayerNormKernel.cu @@ -95,7 +95,6 @@ __global__ void skiplnDQQ(const int ld, const int8_t* input, const int8_t* skip, } copy(out_local, &output[idx]); - } template diff --git a/plugin/skipLayerNormPlugin/skipLayerNormPlugin.cpp b/plugin/skipLayerNormPlugin/skipLayerNormPlugin.cpp index 2521790d..f2eaebac 100644 --- a/plugin/skipLayerNormPlugin/skipLayerNormPlugin.cpp +++ b/plugin/skipLayerNormPlugin/skipLayerNormPlugin.cpp @@ -33,9 +33,9 @@ namespace bert // Clip plugin specific constants namespace { -static const char* SKIP_LAYER_NORM_VERSION{"1"}; -static const char* SKIP_LAYER_NORM_NAME{"CustomSkipLayerNormPluginDynamic"}; -static const char* SKIP_LAYER_NORM_VAR_SEQLEN_VERSION{"2"}; +const char* SKIP_LAYER_NORM_VERSION{"1"}; +const char* SKIP_LAYER_NORM_NAME{"CustomSkipLayerNormPluginDynamic"}; +const char* SKIP_LAYER_NORM_VAR_SEQLEN_VERSION{"2"}; } // namespace // Static class fields initialization @@ -48,7 +48,7 @@ std::vector SkipLayerNormVarSeqlenPluginCreator::mPluginAttributes; REGISTER_TENSORRT_PLUGIN(SkipLayerNormPluginDynamicCreator); REGISTER_TENSORRT_PLUGIN(SkipLayerNormVarSeqlenPluginCreator); -static inline DataType getParamWordType(DataType cfgType) +static inline DataType getParamWordType(DataType cfgType) noexcept { if (cfgType == DataType::kINT8) { @@ -67,11 +67,10 @@ SkipLayerNormPluginDynamic::SkipLayerNormPluginDynamic(const std::string name, c , mType(type) , mBiasDev(nullptr) { - assert(mType == nvinfer1::DataType::kFLOAT || mType == nvinfer1::DataType::kHALF - || mType == nvinfer1::DataType::kINT8); + assert(mType == nvinfer1::DataType::kFLOAT || mType == nvinfer1::DataType::kHALF || mType == nvinfer1::DataType::kINT8); // mCfgType is the dataType for beta, gamma bias weights, always fp16 or fp32 // mType is the plugin IO datatype, can be int8 - mCfgType = mType == DataType::kINT8 ? DataType::kHALF : mType; + mCfgType = mType == DataType::kINT8 ? DataType::kHALF : mType; mParamWordsize = getElementSize(mCfgType); mBeta.convertAndCopy(beta, mCfgType); @@ -111,18 +110,18 @@ SkipLayerNormPluginDynamic::SkipLayerNormPluginDynamic(const std::string name, c } // IPluginV2DynamicExt Methods -IPluginV2DynamicExt* SkipLayerNormPluginDynamic::clone() const +IPluginV2DynamicExt* SkipLayerNormPluginDynamic::clone() const noexcept { gLogVerbose << "SkipLayerNormPluginDynamic clone\n"; - auto p = new SkipLayerNormPluginDynamic(mLayerName, mType, mLd, mBeta, mGamma, mBias); + auto* p = new SkipLayerNormPluginDynamic(mLayerName, mType, mLd, mBeta, mGamma, mBias); p->initialize(); p->setPluginNamespace(mNamespace.c_str()); return p; } DimsExprs SkipLayerNormPluginDynamic::getOutputDimensions( - int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) + int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) noexcept { assert(nbInputs == 2); assert(outputIndex == 0); @@ -131,7 +130,7 @@ DimsExprs SkipLayerNormPluginDynamic::getOutputDimensions( } bool SkipLayerNormPluginDynamic::supportsFormatCombination( - int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) + int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept { assert(nbInputs == 2); assert(nbOutputs == 1); @@ -166,7 +165,7 @@ bool SkipLayerNormPluginDynamic::supportsFormatCombination( } void SkipLayerNormPluginDynamic::configurePlugin( - const DynamicPluginTensorDesc* inputs, int nbInputs, const DynamicPluginTensorDesc* outputs, int nbOutputs) + const DynamicPluginTensorDesc* inputs, int nbInputs, const DynamicPluginTensorDesc* outputs, int nbOutputs) noexcept { gLogVerbose << "SkipLayerNormPluginDynamic configurePlugin\n"; @@ -209,13 +208,13 @@ void SkipLayerNormPluginDynamic::configurePlugin( } size_t SkipLayerNormPluginDynamic::getWorkspaceSize( - const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const + const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const noexcept { return 0; } int SkipLayerNormPluginDynamic::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { const int inputVolume = volume(inputDesc[0].dims); int status = -1; @@ -225,12 +224,12 @@ int SkipLayerNormPluginDynamic::enqueue(const PluginTensorDesc* inputDesc, const // Launch CUDA kernel wrapper and save its return value if (iType == DataType::kFLOAT) { - const auto input = static_cast(inputs[0]); - const auto skip = static_cast(inputs[1]); - auto output = static_cast(outputs[0]); - const auto bias = static_cast(mBiasDev.get()); - const auto beta = static_cast(mBetaDev.get()); - const auto gamma = static_cast(mGammaDev.get()); + const auto* const input = static_cast(inputs[0]); + const auto* const skip = static_cast(inputs[1]); + auto* output = static_cast(outputs[0]); + const auto* const bias = static_cast(mBiasDev.get()); + const auto* const beta = static_cast(mBetaDev.get()); + const auto* const gamma = static_cast(mGammaDev.get()); if (mHasBias) { status = computeSkipLayerNorm( @@ -238,18 +237,18 @@ int SkipLayerNormPluginDynamic::enqueue(const PluginTensorDesc* inputDesc, const } else { - status = computeSkipLayerNorm( - stream, static_cast(mLd), inputVolume, input, skip, beta, gamma, output, bias); + status + = computeSkipLayerNorm(stream, static_cast(mLd), inputVolume, input, skip, beta, gamma, output, bias); } } else if (iType == DataType::kHALF) { - const auto input = static_cast(inputs[0]); - const auto skip = static_cast(inputs[1]); - auto output = static_cast(outputs[0]); - const auto bias = static_cast(mBiasDev.get()); - const auto beta = static_cast(mBetaDev.get()); - const auto gamma = static_cast(mGammaDev.get()); + const auto* const input = static_cast(inputs[0]); + const auto* const skip = static_cast(inputs[1]); + auto* output = static_cast(outputs[0]); + const auto* const bias = static_cast(mBiasDev.get()); + const auto* const beta = static_cast(mBetaDev.get()); + const auto* const gamma = static_cast(mGammaDev.get()); if (mHasBias) { status = computeSkipLayerNorm( @@ -257,21 +256,21 @@ int SkipLayerNormPluginDynamic::enqueue(const PluginTensorDesc* inputDesc, const } else { - status = computeSkipLayerNorm( - stream, static_cast(mLd), inputVolume, input, skip, beta, gamma, output, bias); + status + = computeSkipLayerNorm(stream, static_cast(mLd), inputVolume, input, skip, beta, gamma, output, bias); } } else if (iType == DataType::kINT8) { const float dqScaleIn = inputDesc[0].scale; const float dqScaleSkip = inputDesc[1].scale; - const float qScale = 1.f / outputDesc[0].scale; - const auto input = static_cast(inputs[0]); - const auto skip = static_cast(inputs[1]); - auto output = static_cast(outputs[0]); - const auto bias = static_cast(mBiasDev.get()); - const auto beta = static_cast(mBetaDev.get()); - const auto gamma = static_cast(mGammaDev.get()); + const float qScale = 1.F / outputDesc[0].scale; + const auto* const input = static_cast(inputs[0]); + const auto* const skip = static_cast(inputs[1]); + auto* output = static_cast(outputs[0]); + const auto* const bias = static_cast(mBiasDev.get()); + const auto* const beta = static_cast(mBetaDev.get()); + const auto* const gamma = static_cast(mGammaDev.get()); if (mHasBias) { status = computeSkipLayerNormDQQ(stream, static_cast(mLd), inputVolume, input, skip, beta, gamma, @@ -279,21 +278,20 @@ int SkipLayerNormPluginDynamic::enqueue(const PluginTensorDesc* inputDesc, const } else { - status = computeSkipLayerNormDQQ(stream, static_cast(mLd), inputVolume, input, skip, beta, - gamma, output, bias, dqScaleIn, dqScaleSkip, qScale); + status = computeSkipLayerNormDQQ( + stream, static_cast(mLd), inputVolume, input, skip, beta, gamma, output, bias, dqScaleIn, dqScaleSkip, qScale); } } else { - gLogError << "Unsupported type error, expected [kINT8,kHALF,kFLOAT], but received " << static_cast(iType) - << "." << std::endl; + gLogError << "Unsupported type error, expected [kINT8,kHALF,kFLOAT], but received " << static_cast(iType) << "." << std::endl; assert(false); } return status; } // IPluginV2Ext Methods -DataType SkipLayerNormPluginDynamic::getOutputDataType(int index, const DataType* inputTypes, int nbInputs) const +DataType SkipLayerNormPluginDynamic::getOutputDataType(int index, const DataType* inputTypes, int nbInputs) const noexcept { assert(index == 0); assert(nbInputs == 2); @@ -301,38 +299,38 @@ DataType SkipLayerNormPluginDynamic::getOutputDataType(int index, const DataType } // IPluginV2 Methods -const char* SkipLayerNormPluginDynamic::getPluginType() const +const char* SkipLayerNormPluginDynamic::getPluginType() const noexcept { return SKIP_LAYER_NORM_NAME; } -const char* SkipLayerNormPluginDynamic::getPluginVersion() const +const char* SkipLayerNormPluginDynamic::getPluginVersion() const noexcept { return SKIP_LAYER_NORM_VERSION; } -int SkipLayerNormPluginDynamic::getNbOutputs() const +int SkipLayerNormPluginDynamic::getNbOutputs() const noexcept { return 1; } -int SkipLayerNormPluginDynamic::initialize() +int SkipLayerNormPluginDynamic::initialize() noexcept { gLogVerbose << "SkipLayerNormPluginDynamic initialize\n"; return 0; } -void SkipLayerNormPluginDynamic::terminate() +void SkipLayerNormPluginDynamic::terminate() noexcept { gLogVerbose << "SkipLayerNormPluginDynamic terminate\n"; } -size_t SkipLayerNormPluginDynamic::getSerializationSize() const +size_t SkipLayerNormPluginDynamic::getSerializationSize() const noexcept { const size_t biasSize = mHasBias ? (mLd * mParamWordsize) : 0; return 2 * mParamWordsize * mLd + 2 * sizeof(DataType) + sizeof(mLd) + biasSize + sizeof(mHasBias); } -void SkipLayerNormPluginDynamic::serialize(void* buffer) const +void SkipLayerNormPluginDynamic::serialize(void* buffer) const noexcept { serialize_value(&buffer, mType); serialize_value(&buffer, mCfgType); @@ -348,22 +346,22 @@ void SkipLayerNormPluginDynamic::serialize(void* buffer) const } } -void SkipLayerNormPluginDynamic::destroy() +void SkipLayerNormPluginDynamic::destroy() noexcept { gLogVerbose << "SkipLayerNormPluginDynamic destroy\n"; // This gets called when the network containing plugin is destroyed - mGammaDev.release(); - mBetaDev.release(); - mBiasDev.release(); + mGammaDev.reset(nullptr); + mBetaDev.reset(nullptr); + mBiasDev.reset(nullptr); delete this; } -void SkipLayerNormPluginDynamic::setPluginNamespace(const char* libNamespace) +void SkipLayerNormPluginDynamic::setPluginNamespace(const char* libNamespace) noexcept { mNamespace = libNamespace; } -const char* SkipLayerNormPluginDynamic::getPluginNamespace() const +const char* SkipLayerNormPluginDynamic::getPluginNamespace() const noexcept { return mNamespace.c_str(); } @@ -376,104 +374,120 @@ SkipLayerNormPluginDynamicCreator::SkipLayerNormPluginDynamicCreator() mFC.fields = mPluginAttributes.data(); } -const char* SkipLayerNormPluginDynamicCreator::getPluginName() const +const char* SkipLayerNormPluginDynamicCreator::getPluginName() const noexcept { return SKIP_LAYER_NORM_NAME; } -const char* SkipLayerNormPluginDynamicCreator::getPluginVersion() const +const char* SkipLayerNormPluginDynamicCreator::getPluginVersion() const noexcept { return SKIP_LAYER_NORM_VERSION; } -const PluginFieldCollection* SkipLayerNormPluginDynamicCreator::getFieldNames() +const PluginFieldCollection* SkipLayerNormPluginDynamicCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2* SkipLayerNormPluginDynamicCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2* SkipLayerNormPluginDynamicCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { - gLogVerbose << "SkipLayerNormPluginDynamicCreator createPlugin\n"; - - int ld = 0; - Weights beta{DataType::kFLOAT, nullptr, 0}; - Weights gamma{DataType::kFLOAT, nullptr, 0}; - Weights bias{DataType::kFLOAT, nullptr, 0}; - int typeId = -1; - - for (int i = 0; i < fc->nbFields; i++) + try { - std::string field_name(fc->fields[i].name); - if (field_name.compare("ld") == 0) + gLogVerbose << "SkipLayerNormPluginDynamicCreator createPlugin\n"; + + int ld = 0; + Weights beta{DataType::kFLOAT, nullptr, 0}; + Weights gamma{DataType::kFLOAT, nullptr, 0}; + Weights bias{DataType::kFLOAT, nullptr, 0}; + int typeId = -1; + + for (int i = 0; i < fc->nbFields; i++) { - ld = *static_cast(fc->fields[i].data); - gLogVerbose << "Building ld: " << ld << std::endl; + std::string field_name(fc->fields[i].name); + if (field_name.compare("ld") == 0) + { + ld = *static_cast(fc->fields[i].data); + gLogVerbose << "Building ld: " << ld << std::endl; + } + + if (field_name.compare("type_id") == 0) + { + typeId = *static_cast(fc->fields[i].data); + gLogVerbose << "Building typeId: " << typeId << std::endl; + } + + if (field_name.compare("beta") == 0) + { + gLogVerbose << "Building beta...\n"; + beta.values = fc->fields[i].data; + beta.count = fc->fields[i].length; + beta.type = fieldTypeToDataType(fc->fields[i].type); + } + + if (field_name.compare("gamma") == 0) + { + gLogVerbose << "Building gamma...\n"; + gamma.values = fc->fields[i].data; + gamma.count = fc->fields[i].length; + gamma.type = fieldTypeToDataType(fc->fields[i].type); + } + + if (field_name.compare("bias") == 0) + { + gLogVerbose << "Building bias...\n"; + bias.values = fc->fields[i].data; + bias.count = fc->fields[i].length; + bias.type = fieldTypeToDataType(fc->fields[i].type); + } + } + gLogVerbose << "Type " << typeId << std::endl; + + if (typeId < 0 || typeId > 3) + { + gLogError << "SkipLayerNorm: Invalid type ID: " << typeId << std::endl; } - if (field_name.compare("type_id") == 0) + if (beta.count <= 0 || beta.values == nullptr) { - typeId = *static_cast(fc->fields[i].data); - gLogVerbose << "Building typeId: " << typeId << std::endl; + gLogError << "SkipLayerNorm: invalid beta" << std::endl; } - if (field_name.compare("beta") == 0) + if (gamma.count <= 0 || gamma.values == nullptr) { - gLogVerbose << "Building beta...\n"; - beta.values = fc->fields[i].data; - beta.count = fc->fields[i].length; - beta.type = fieldTypeToDataType(fc->fields[i].type); + gLogError << "SkipLayerNorm: invalid gamma" << std::endl; } - if (field_name.compare("gamma") == 0) - { - gLogVerbose << "Building gamma...\n"; - gamma.values = fc->fields[i].data; - gamma.count = fc->fields[i].length; - gamma.type = fieldTypeToDataType(fc->fields[i].type); - } - - if (field_name.compare("bias") == 0) - { - gLogVerbose << "Building bias...\n"; - bias.values = fc->fields[i].data; - bias.count = fc->fields[i].length; - bias.type = fieldTypeToDataType(fc->fields[i].type); - } + return new SkipLayerNormPluginDynamic(name, static_cast(typeId), ld, beta, gamma, bias); } - gLogVerbose << "Type " << typeId << std::endl; - - if (typeId < 0 || typeId > 3) + catch (const std::exception& e) { - gLogError << "SkipLayerNorm: Invalid type ID: " << typeId << std::endl; + caughtError(e); } - - if (beta.count <= 0 || beta.values == nullptr) - { - gLogError << "SkipLayerNorm: invalid beta" << std::endl; - } - - if (gamma.count <= 0 || gamma.values == nullptr) - { - gLogError << "SkipLayerNorm: invalid gamma" << std::endl; - } - - return new SkipLayerNormPluginDynamic(name, static_cast(typeId), ld, beta, gamma, bias); + return nullptr; } IPluginV2* SkipLayerNormPluginDynamicCreator::deserializePlugin( - const char* name, const void* serialData, size_t serialLength) + const char* name, const void* serialData, size_t serialLength) noexcept { // This object will be deleted when the network is destroyed, which will // call SkipLayerNormPluginDynamic::destroy() - return new SkipLayerNormPluginDynamic(name, serialData, serialLength); + try + { + return new SkipLayerNormPluginDynamic(name, serialData, serialLength); + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } -void SkipLayerNormPluginDynamicCreator::setPluginNamespace(const char* libNamespace) +void SkipLayerNormPluginDynamicCreator::setPluginNamespace(const char* libNamespace) noexcept { mNamespace = libNamespace; } -const char* SkipLayerNormPluginDynamicCreator::getPluginNamespace() const +const char* SkipLayerNormPluginDynamicCreator::getPluginNamespace() const noexcept { return mNamespace.c_str(); } @@ -490,11 +504,10 @@ SkipLayerNormVarSeqlenPlugin::SkipLayerNormVarSeqlenPlugin( { assert(mLd > 0); assert(beta.count == gamma.count); - assert(mType == nvinfer1::DataType::kFLOAT || mType == nvinfer1::DataType::kHALF - || mType == nvinfer1::DataType::kINT8); + assert(mType == nvinfer1::DataType::kFLOAT || mType == nvinfer1::DataType::kHALF || mType == nvinfer1::DataType::kINT8); // mCfgType is the dataType for beta, gamma bias weights, always fp16 or fp32 // mType is the plugin IO datatype, can be int8 - mCfgType = mType == DataType::kINT8 ? DataType::kHALF : mType; + mCfgType = mType == DataType::kINT8 ? DataType::kHALF : mType; mParamWordsize = getElementSize(mCfgType); mBeta.convertAndCopy(beta, mCfgType); @@ -505,6 +518,7 @@ SkipLayerNormVarSeqlenPlugin::SkipLayerNormVarSeqlenPlugin( { mBias.convertAndCopy(bias, mCfgType); } + } SkipLayerNormVarSeqlenPlugin::SkipLayerNormVarSeqlenPlugin(const std::string name, const void* data, size_t length) @@ -535,18 +549,18 @@ SkipLayerNormVarSeqlenPlugin::SkipLayerNormVarSeqlenPlugin(const std::string nam } // IPluginV2DynamicExt Methods -IPluginV2DynamicExt* SkipLayerNormVarSeqlenPlugin::clone() const +IPluginV2DynamicExt* SkipLayerNormVarSeqlenPlugin::clone() const noexcept { gLogVerbose << "SkipLayerNormVarSeqlenPlugin clone\n"; - auto p = new SkipLayerNormVarSeqlenPlugin(mLayerName, mType, mBeta, mGamma, mBias); + auto* p = new SkipLayerNormVarSeqlenPlugin(mLayerName, mType, mBeta, mGamma, mBias); p->initialize(); p->setPluginNamespace(mNamespace.c_str()); return p; } DimsExprs SkipLayerNormVarSeqlenPlugin::getOutputDimensions( - int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) + int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) noexcept { assert(nbInputs == 2); assert(outputIndex == 0); @@ -555,15 +569,14 @@ DimsExprs SkipLayerNormVarSeqlenPlugin::getOutputDimensions( } bool SkipLayerNormVarSeqlenPlugin::supportsFormatCombination( - int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) + int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept { assert(nbInputs == 2); assert(nbOutputs == 1); const PluginTensorDesc& in = inOut[pos]; - if (mType != in.type) - return false; + if(mType != in.type) return false; if (pos == 0) { // Since H = W = 1, we can report CHWx for any x @@ -592,23 +605,8 @@ bool SkipLayerNormVarSeqlenPlugin::supportsFormatCombination( return in.format == prev.format; } -void SkipLayerNormVarSeqlenPlugin::copyParamToDevice() -{ - if (!mParamsOnDevice) - { - const auto paramType = getParamWordType(mCfgType); - copyToDevice(mGamma, getWeightsSize(mGamma, paramType), mGammaDev); - copyToDevice(mBeta, getWeightsSize(mBeta, paramType), mBetaDev); - if (mHasBias) - { - copyToDevice(mBias, getWeightsSize(mBias, paramType), mBiasDev); - } - mParamsOnDevice = true; - } -} - void SkipLayerNormVarSeqlenPlugin::configurePlugin( - const DynamicPluginTensorDesc* inputs, int nbInputs, const DynamicPluginTensorDesc* outputs, int nbOutputs) + const DynamicPluginTensorDesc* inputs, int nbInputs, const DynamicPluginTensorDesc* outputs, int nbOutputs) noexcept { // Validate input arguments assert(nbOutputs == 1); @@ -635,36 +633,42 @@ void SkipLayerNormVarSeqlenPlugin::configurePlugin( const auto paramType = getParamWordType(mCfgType); mParamWordsize = getElementSize(paramType); - copyParamToDevice(); + if (!mParamsOnDevice) + { + copyToDevice(mGamma, getWeightsSize(mGamma, paramType), mGammaDev); + copyToDevice(mBeta, getWeightsSize(mBeta, paramType), mBetaDev); + if (mHasBias) + { + copyToDevice(mBias, getWeightsSize(mBias, paramType), mBiasDev); + } + mParamsOnDevice = true; + } } size_t SkipLayerNormVarSeqlenPlugin::getWorkspaceSize( - const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const + const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const noexcept { return 0; } int SkipLayerNormVarSeqlenPlugin::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { const int inputVolume = volume(inputDesc[0].dims); assert(inputVolume % mLd == 0 && "inconsistent dimensions"); int status = -1; DataType iType = inputDesc->type; - // WAR to work with TRT6.0 - copyParamToDevice(); - // Our plugin outputs only one tensor // Launch CUDA kernel wrapper and save its return value if (iType == DataType::kFLOAT) { - const auto input = static_cast(inputs[0]); - const auto skip = static_cast(inputs[1]); - auto output = static_cast(outputs[0]); - const auto bias = static_cast(mBiasDev.get()); - const auto beta = static_cast(mBetaDev.get()); - const auto gamma = static_cast(mGammaDev.get()); + const auto* const input = static_cast(inputs[0]); + const auto* const skip = static_cast(inputs[1]); + auto* output = static_cast(outputs[0]); + const auto* const bias = static_cast(mBiasDev.get()); + const auto* const beta = static_cast(mBetaDev.get()); + const auto* const gamma = static_cast(mGammaDev.get()); if (mHasBias) { status = computeSkipLayerNorm( @@ -672,18 +676,18 @@ int SkipLayerNormVarSeqlenPlugin::enqueue(const PluginTensorDesc* inputDesc, con } else { - status = computeSkipLayerNorm( - stream, static_cast(mLd), inputVolume, input, skip, beta, gamma, output, bias); + status + = computeSkipLayerNorm(stream, static_cast(mLd), inputVolume, input, skip, beta, gamma, output, bias); } } else if (iType == DataType::kHALF) { - const auto input = static_cast(inputs[0]); - const auto skip = static_cast(inputs[1]); - auto output = static_cast(outputs[0]); - const auto bias = static_cast(mBiasDev.get()); - const auto beta = static_cast(mBetaDev.get()); - const auto gamma = static_cast(mGammaDev.get()); + const auto* const input = static_cast(inputs[0]); + const auto* const skip = static_cast(inputs[1]); + auto* output = static_cast(outputs[0]); + const auto* const bias = static_cast(mBiasDev.get()); + const auto* const beta = static_cast(mBetaDev.get()); + const auto* const gamma = static_cast(mGammaDev.get()); if (mHasBias) { status = computeSkipLayerNorm( @@ -691,21 +695,21 @@ int SkipLayerNormVarSeqlenPlugin::enqueue(const PluginTensorDesc* inputDesc, con } else { - status = computeSkipLayerNorm( - stream, static_cast(mLd), inputVolume, input, skip, beta, gamma, output, bias); + status + = computeSkipLayerNorm(stream, static_cast(mLd), inputVolume, input, skip, beta, gamma, output, bias); } } else if (iType == DataType::kINT8) { const float dqScaleIn = inputDesc[0].scale; const float dqScaleSkip = inputDesc[1].scale; - const float qScale = 1.f / outputDesc[0].scale; - const auto input = static_cast(inputs[0]); - const auto skip = static_cast(inputs[1]); - auto output = static_cast(outputs[0]); - const auto bias = static_cast(mBiasDev.get()); - const auto beta = static_cast(mBetaDev.get()); - const auto gamma = static_cast(mGammaDev.get()); + const float qScale = 1.F / outputDesc[0].scale; + const auto* const input = static_cast(inputs[0]); + const auto* const skip = static_cast(inputs[1]); + auto* output = static_cast(outputs[0]); + const auto* const bias = static_cast(mBiasDev.get()); + const auto* const beta = static_cast(mBetaDev.get()); + const auto* const gamma = static_cast(mGammaDev.get()); if (mHasBias) { status = computeSkipLayerNormDQQ(stream, static_cast(mLd), inputVolume, input, skip, beta, gamma, @@ -713,21 +717,20 @@ int SkipLayerNormVarSeqlenPlugin::enqueue(const PluginTensorDesc* inputDesc, con } else { - status = computeSkipLayerNormDQQ(stream, static_cast(mLd), inputVolume, input, skip, beta, - gamma, output, bias, dqScaleIn, dqScaleSkip, qScale); + status = computeSkipLayerNormDQQ( + stream, static_cast(mLd), inputVolume, input, skip, beta, gamma, output, bias, dqScaleIn, dqScaleSkip, qScale); } } else { - gLogError << "Unsupported type error, expected [kINT8,kHALF,kFLOAT], but received " << static_cast(iType) - << "." << std::endl; + gLogError << "Unsupported type error, expected [kINT8,kHALF,kFLOAT], but received " << static_cast(iType) << "." << std::endl; assert(false); } return status; } // IPluginV2Ext Methods -DataType SkipLayerNormVarSeqlenPlugin::getOutputDataType(int index, const DataType* inputTypes, int nbInputs) const +DataType SkipLayerNormVarSeqlenPlugin::getOutputDataType(int index, const DataType* inputTypes, int nbInputs) const noexcept { assert(index == 0); assert(nbInputs == 2); @@ -735,38 +738,38 @@ DataType SkipLayerNormVarSeqlenPlugin::getOutputDataType(int index, const DataTy } // IPluginV2 Methods -const char* SkipLayerNormVarSeqlenPlugin::getPluginType() const +const char* SkipLayerNormVarSeqlenPlugin::getPluginType() const noexcept { return SKIP_LAYER_NORM_NAME; } -const char* SkipLayerNormVarSeqlenPlugin::getPluginVersion() const +const char* SkipLayerNormVarSeqlenPlugin::getPluginVersion() const noexcept { return SKIP_LAYER_NORM_VAR_SEQLEN_VERSION; } -int SkipLayerNormVarSeqlenPlugin::getNbOutputs() const +int SkipLayerNormVarSeqlenPlugin::getNbOutputs() const noexcept { return 1; } -int SkipLayerNormVarSeqlenPlugin::initialize() +int SkipLayerNormVarSeqlenPlugin::initialize() noexcept { gLogVerbose << "SkipLayerNormVarSeqlenPlugin initialize\n"; return 0; } -void SkipLayerNormVarSeqlenPlugin::terminate() +void SkipLayerNormVarSeqlenPlugin::terminate() noexcept { gLogVerbose << "SkipLayerNormVarSeqlenPlugin terminate\n"; } -size_t SkipLayerNormVarSeqlenPlugin::getSerializationSize() const +size_t SkipLayerNormVarSeqlenPlugin::getSerializationSize() const noexcept { const size_t biasSize = mHasBias ? (mLd * mParamWordsize) : 0; return 2 * mParamWordsize * mLd + 2 * sizeof(DataType) + sizeof(mLd) + biasSize + sizeof(mHasBias); } -void SkipLayerNormVarSeqlenPlugin::serialize(void* buffer) const +void SkipLayerNormVarSeqlenPlugin::serialize(void* buffer) const noexcept { serialize_value(&buffer, mType); serialize_value(&buffer, mCfgType); @@ -782,22 +785,22 @@ void SkipLayerNormVarSeqlenPlugin::serialize(void* buffer) const } } -void SkipLayerNormVarSeqlenPlugin::destroy() +void SkipLayerNormVarSeqlenPlugin::destroy() noexcept { gLogVerbose << "SkipLayerNormVarSeqlenPlugin destroy\n"; // This gets called when the network containing plugin is destroyed - mGammaDev.release(); - mBetaDev.release(); - mBiasDev.release(); + mGammaDev.reset(nullptr); + mBetaDev.reset(nullptr); + mBiasDev.reset(nullptr); delete this; } -void SkipLayerNormVarSeqlenPlugin::setPluginNamespace(const char* libNamespace) +void SkipLayerNormVarSeqlenPlugin::setPluginNamespace(const char* libNamespace) noexcept { mNamespace = libNamespace; } -const char* SkipLayerNormVarSeqlenPlugin::getPluginNamespace() const +const char* SkipLayerNormVarSeqlenPlugin::getPluginNamespace() const noexcept { return mNamespace.c_str(); } @@ -810,98 +813,114 @@ SkipLayerNormVarSeqlenPluginCreator::SkipLayerNormVarSeqlenPluginCreator() mFC.fields = mPluginAttributes.data(); } -const char* SkipLayerNormVarSeqlenPluginCreator::getPluginName() const +const char* SkipLayerNormVarSeqlenPluginCreator::getPluginName() const noexcept { return SKIP_LAYER_NORM_NAME; } -const char* SkipLayerNormVarSeqlenPluginCreator::getPluginVersion() const +const char* SkipLayerNormVarSeqlenPluginCreator::getPluginVersion() const noexcept { return SKIP_LAYER_NORM_VAR_SEQLEN_VERSION; } -const PluginFieldCollection* SkipLayerNormVarSeqlenPluginCreator::getFieldNames() +const PluginFieldCollection* SkipLayerNormVarSeqlenPluginCreator::getFieldNames() noexcept { return &mFC; } -IPluginV2* SkipLayerNormVarSeqlenPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) +IPluginV2* SkipLayerNormVarSeqlenPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { - gLogVerbose << "SkipLayerNormVarSeqlenPluginCreator createPlugin\n"; - - Weights beta{DataType::kFLOAT, nullptr, 0}; - Weights gamma{DataType::kFLOAT, nullptr, 0}; - Weights bias{DataType::kFLOAT, nullptr, 0}; - int typeId = -1; - - for (int i = 0; i < fc->nbFields; i++) + try { - std::string field_name(fc->fields[i].name); + gLogVerbose << "SkipLayerNormVarSeqlenPluginCreator createPlugin\n"; - if (field_name.compare("type_id") == 0) + Weights beta{DataType::kFLOAT, nullptr, 0}; + Weights gamma{DataType::kFLOAT, nullptr, 0}; + Weights bias{DataType::kFLOAT, nullptr, 0}; + int typeId = -1; + + for (int i = 0; i < fc->nbFields; i++) { - typeId = *static_cast(fc->fields[i].data); - gLogVerbose << "Building typeId: " << typeId << std::endl; + std::string field_name(fc->fields[i].name); + + if (field_name.compare("type_id") == 0) + { + typeId = *static_cast(fc->fields[i].data); + gLogVerbose << "Building typeId: " << typeId << std::endl; + } + + if (field_name.compare("beta") == 0) + { + gLogVerbose << "Building beta...\n"; + beta.values = fc->fields[i].data; + beta.count = fc->fields[i].length; + beta.type = fieldTypeToDataType(fc->fields[i].type); + } + + if (field_name.compare("gamma") == 0) + { + gLogVerbose << "Building gamma...\n"; + gamma.values = fc->fields[i].data; + gamma.count = fc->fields[i].length; + gamma.type = fieldTypeToDataType(fc->fields[i].type); + } + + if (field_name.compare("bias") == 0) + { + gLogVerbose << "Building bias...\n"; + bias.values = fc->fields[i].data; + bias.count = fc->fields[i].length; + bias.type = fieldTypeToDataType(fc->fields[i].type); + } + } + gLogVerbose << "Type " << typeId << std::endl; + + if (typeId < 0 || typeId > 3) + { + gLogError << "SkipLayerNorm: Invalid type ID: " << typeId << std::endl; } - if (field_name.compare("beta") == 0) + if (beta.count <= 0 || beta.values == nullptr) { - gLogVerbose << "Building beta...\n"; - beta.values = fc->fields[i].data; - beta.count = fc->fields[i].length; - beta.type = fieldTypeToDataType(fc->fields[i].type); + gLogError << "SkipLayerNorm: invalid beta" << std::endl; } - if (field_name.compare("gamma") == 0) + if (gamma.count <= 0 || gamma.values == nullptr) { - gLogVerbose << "Building gamma...\n"; - gamma.values = fc->fields[i].data; - gamma.count = fc->fields[i].length; - gamma.type = fieldTypeToDataType(fc->fields[i].type); + gLogError << "SkipLayerNorm: invalid gamma" << std::endl; } - if (field_name.compare("bias") == 0) - { - gLogVerbose << "Building bias...\n"; - bias.values = fc->fields[i].data; - bias.count = fc->fields[i].length; - bias.type = fieldTypeToDataType(fc->fields[i].type); - } + return new SkipLayerNormVarSeqlenPlugin(name, static_cast(typeId), beta, gamma, bias); } - gLogVerbose << "Type " << typeId << std::endl; - - if (typeId < 0 || typeId > 3) + catch (const std::exception& e) { - gLogError << "SkipLayerNorm: Invalid type ID: " << typeId << std::endl; + caughtError(e); } - - if (beta.count <= 0 || beta.values == nullptr) - { - gLogError << "SkipLayerNorm: invalid beta" << std::endl; - } - - if (gamma.count <= 0 || gamma.values == nullptr) - { - gLogError << "SkipLayerNorm: invalid gamma" << std::endl; - } - - return new SkipLayerNormVarSeqlenPlugin(name, static_cast(typeId), beta, gamma, bias); + return nullptr; } IPluginV2* SkipLayerNormVarSeqlenPluginCreator::deserializePlugin( - const char* name, const void* serialData, size_t serialLength) + const char* name, const void* serialData, size_t serialLength) noexcept { // This object will be deleted when the network is destroyed, which will // call SkipLayerNormVarSeqlenPlugin::destroy() - return new SkipLayerNormVarSeqlenPlugin(name, serialData, serialLength); + try + { + return new SkipLayerNormVarSeqlenPlugin(name, serialData, serialLength); + } + catch (const std::exception& e) + { + caughtError(e); + } + return nullptr; } -void SkipLayerNormVarSeqlenPluginCreator::setPluginNamespace(const char* libNamespace) +void SkipLayerNormVarSeqlenPluginCreator::setPluginNamespace(const char* libNamespace) noexcept { mNamespace = libNamespace; } -const char* SkipLayerNormVarSeqlenPluginCreator::getPluginNamespace() const +const char* SkipLayerNormVarSeqlenPluginCreator::getPluginNamespace() const noexcept { return mNamespace.c_str(); } diff --git a/plugin/skipLayerNormPlugin/skipLayerNormPlugin.h b/plugin/skipLayerNormPlugin/skipLayerNormPlugin.h index c20584dd..454719c7 100644 --- a/plugin/skipLayerNormPlugin/skipLayerNormPlugin.h +++ b/plugin/skipLayerNormPlugin/skipLayerNormPlugin.h @@ -38,11 +38,12 @@ template int computeSkipLayerNorm(cudaStream_t stream, const int ld, const int n, const T* input, const T* skip, const T* beta, const T* gamma, T* output, const T* bias); + class SkipLayerNormPluginDynamic : public nvinfer1::IPluginV2DynamicExt { public: - SkipLayerNormPluginDynamic(const std::string name, const nvinfer1::DataType type, const int ld, - const nvinfer1::Weights& beta, const nvinfer1::Weights& gamma, const nvinfer1::Weights& bias); + SkipLayerNormPluginDynamic(const std::string name, const nvinfer1::DataType type, const int ld, const nvinfer1::Weights& beta, + const nvinfer1::Weights& gamma, const nvinfer1::Weights& bias); SkipLayerNormPluginDynamic(const std::string name, const void* data, size_t length); @@ -51,32 +52,33 @@ public: SkipLayerNormPluginDynamic() = delete; // IPluginV2DynamicExt Methods - nvinfer1::IPluginV2DynamicExt* clone() const override; - nvinfer1::DimsExprs getOutputDimensions( - int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) override; + nvinfer1::IPluginV2DynamicExt* clone() const noexcept override; + nvinfer1::DimsExprs getOutputDimensions(int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, + nvinfer1::IExprBuilder& exprBuilder) noexcept override; bool supportsFormatCombination( - int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) override; + int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept override; void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, - const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) override; + const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) noexcept override; size_t getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int nbInputs, - const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const override; + const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const noexcept override; int enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) override; + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; // IPluginV2Ext Methods - nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const + noexcept override; // IPluginV2 Methods - const char* getPluginType() const override; - const char* getPluginVersion() const override; - int getNbOutputs() const override; - int initialize() override; - void terminate() override; - size_t getSerializationSize() const override; - void serialize(void* buffer) const override; - void destroy() override; - void setPluginNamespace(const char* pluginNamespace) override; - const char* getPluginNamespace() const override; + const char* getPluginType() const noexcept override; + const char* getPluginVersion() const noexcept override; + int getNbOutputs() const noexcept override; + int initialize() noexcept override; + void terminate() noexcept override; + size_t getSerializationSize() const noexcept override; + void serialize(void* buffer) const noexcept override; + void destroy() noexcept override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; + const char* getPluginNamespace() const noexcept override; private: const std::string mLayerName; @@ -95,16 +97,6 @@ private: bert::WeightsWithOwnership mBias; size_t mParamWordsize; - -protected: - // To prevent compiler warnings. - using nvinfer1::IPluginV2DynamicExt::canBroadcastInputAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::configurePlugin; - using nvinfer1::IPluginV2DynamicExt::enqueue; - using nvinfer1::IPluginV2DynamicExt::getOutputDimensions; - using nvinfer1::IPluginV2DynamicExt::getWorkspaceSize; - using nvinfer1::IPluginV2DynamicExt::isOutputBroadcastAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::supportsFormat; }; class SkipLayerNormPluginDynamicCreator : public nvinfer1::IPluginCreator @@ -112,19 +104,19 @@ class SkipLayerNormPluginDynamicCreator : public nvinfer1::IPluginCreator public: SkipLayerNormPluginDynamicCreator(); - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const nvinfer1::PluginFieldCollection* getFieldNames() override; + const nvinfer1::PluginFieldCollection* getFieldNames() noexcept override; - nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) override; + nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) noexcept override; - nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; private: static nvinfer1::PluginFieldCollection mFC; @@ -145,35 +137,33 @@ public: SkipLayerNormVarSeqlenPlugin() = delete; // IPluginV2DynamicExt Methods - nvinfer1::IPluginV2DynamicExt* clone() const override; - nvinfer1::DimsExprs getOutputDimensions( - int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) override; + nvinfer1::IPluginV2DynamicExt* clone() const noexcept override; + nvinfer1::DimsExprs getOutputDimensions(int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, + nvinfer1::IExprBuilder& exprBuilder) noexcept override; bool supportsFormatCombination( - int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) override; + int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept override; void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, - const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) override; + const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) noexcept override; size_t getWorkspaceSize(const nvinfer1::PluginTensorDesc* inputs, int nbInputs, - const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const override; + const nvinfer1::PluginTensorDesc* outputs, int nbOutputs) const noexcept override; int enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) override; + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; // IPluginV2Ext Methods - nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; + nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const + noexcept override; // IPluginV2 Methods - const char* getPluginType() const override; - const char* getPluginVersion() const override; - int getNbOutputs() const override; - int initialize() override; - void terminate() override; - size_t getSerializationSize() const override; - void serialize(void* buffer) const override; - void destroy() override; - void setPluginNamespace(const char* pluginNamespace) override; - const char* getPluginNamespace() const override; - -protected: - void copyParamToDevice(); + const char* getPluginType() const noexcept override; + const char* getPluginVersion() const noexcept override; + int getNbOutputs() const noexcept override; + int initialize() noexcept override; + void terminate() noexcept override; + size_t getSerializationSize() const noexcept override; + void serialize(void* buffer) const noexcept override; + void destroy() noexcept override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; + const char* getPluginNamespace() const noexcept override; private: const std::string mLayerName; @@ -193,16 +183,6 @@ private: size_t mParamWordsize; bool mParamsOnDevice; - -protected: - // To prevent compiler warnings. - using nvinfer1::IPluginV2DynamicExt::canBroadcastInputAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::configurePlugin; - using nvinfer1::IPluginV2DynamicExt::enqueue; - using nvinfer1::IPluginV2DynamicExt::getOutputDimensions; - using nvinfer1::IPluginV2DynamicExt::getWorkspaceSize; - using nvinfer1::IPluginV2DynamicExt::isOutputBroadcastAcrossBatch; - using nvinfer1::IPluginV2DynamicExt::supportsFormat; }; class SkipLayerNormVarSeqlenPluginCreator : public nvinfer1::IPluginCreator @@ -210,19 +190,19 @@ class SkipLayerNormVarSeqlenPluginCreator : public nvinfer1::IPluginCreator public: SkipLayerNormVarSeqlenPluginCreator(); - const char* getPluginName() const override; + const char* getPluginName() const noexcept override; - const char* getPluginVersion() const override; + const char* getPluginVersion() const noexcept override; - const nvinfer1::PluginFieldCollection* getFieldNames() override; + const nvinfer1::PluginFieldCollection* getFieldNames() noexcept override; - nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) override; + nvinfer1::IPluginV2* createPlugin(const char* name, const nvinfer1::PluginFieldCollection* fc) noexcept override; - nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override; + nvinfer1::IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override; - void setPluginNamespace(const char* pluginNamespace) override; + void setPluginNamespace(const char* pluginNamespace) noexcept override; - const char* getPluginNamespace() const override; + const char* getPluginNamespace() const noexcept override; private: static nvinfer1::PluginFieldCollection mFC; diff --git a/plugin/specialSlicePlugin/specialSlicePlugin.cpp b/plugin/specialSlicePlugin/specialSlicePlugin.cpp index 9629e6a9..c9cd487b 100644 --- a/plugin/specialSlicePlugin/specialSlicePlugin.cpp +++ b/plugin/specialSlicePlugin/specialSlicePlugin.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "specialSlicePlugin.h" #include "maskRCNNKernels.h" #include @@ -42,27 +41,27 @@ SpecialSlicePluginCreator::SpecialSlicePluginCreator() noexcept const char* SpecialSlicePluginCreator::getPluginName() const noexcept { return SPECIALSLICE_PLUGIN_NAME; -}; +} const char* SpecialSlicePluginCreator::getPluginVersion() const noexcept { return SPECIALSLICE_PLUGIN_VERSION; -}; +} const PluginFieldCollection* SpecialSlicePluginCreator::getFieldNames() noexcept { return &mFC; -}; +} IPluginV2Ext* SpecialSlicePluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) noexcept { return new SpecialSlice(); -}; +} IPluginV2Ext* SpecialSlicePluginCreator::deserializePlugin(const char* name, const void* data, size_t length) noexcept { return new SpecialSlice(data, length); -}; +} size_t SpecialSlice::getWorkspaceSize(int) const noexcept { @@ -71,30 +70,30 @@ size_t SpecialSlice::getWorkspaceSize(int) const noexcept bool SpecialSlice::supportsFormat(DataType type, PluginFormat format) const noexcept { - return (type == DataType::kFLOAT && format == PluginFormat::kNCHW); -}; + return (type == DataType::kFLOAT && format == PluginFormat::kLINEAR); +} const char* SpecialSlice::getPluginType() const noexcept { return "SpecialSlice_TRT"; -}; +} const char* SpecialSlice::getPluginVersion() const noexcept { return "1"; -}; +} IPluginV2Ext* SpecialSlice::clone() const noexcept { auto plugin = new SpecialSlice(*this); plugin->setPluginNamespace(mNameSpace.c_str()); return plugin; -}; +} void SpecialSlice::setPluginNamespace(const char* libNamespace) noexcept { mNameSpace = libNamespace; -}; +} const char* SpecialSlice::getPluginNamespace() const noexcept { @@ -104,35 +103,35 @@ const char* SpecialSlice::getPluginNamespace() const noexcept size_t SpecialSlice::getSerializationSize() const noexcept { return sizeof(int); -}; +} void SpecialSlice::serialize(void* buffer) const noexcept { char *d = reinterpret_cast(buffer), *a = d; write(d, mBboxesCnt); ASSERT(d == a + getSerializationSize()); -}; +} SpecialSlice::SpecialSlice(const void* data, size_t length) noexcept { const char *d = reinterpret_cast(data), *a = d; mBboxesCnt = read(d); assert(d == a + length); -}; +} -SpecialSlice::SpecialSlice() noexcept { - -}; +SpecialSlice::SpecialSlice() noexcept +{ +} int SpecialSlice::initialize() noexcept { return 0; -}; +} int SpecialSlice::getNbOutputs() const noexcept { return 1; -}; +} void SpecialSlice::check_valid_inputs(const nvinfer1::Dims* inputs, int nbInputDims) noexcept { @@ -157,16 +156,16 @@ Dims SpecialSlice::getOutputDimensions(int index, const Dims* inputDims, int nbI output.d[1] = 4; return output; -}; +} int SpecialSlice::enqueue( - int batch_size, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) noexcept + int batch_size, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { specialSlice(stream, batch_size, mBboxesCnt, inputs[0], outputs[0]); return cudaGetLastError() != cudaSuccess; -}; +} // Return the DataType of the plugin output at the requested index DataType SpecialSlice::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept diff --git a/plugin/specialSlicePlugin/specialSlicePlugin.h b/plugin/specialSlicePlugin/specialSlicePlugin.h index 7d78085f..56133069 100644 --- a/plugin/specialSlicePlugin/specialSlicePlugin.h +++ b/plugin/specialSlicePlugin/specialSlicePlugin.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TRT_SPECIAL_SLICE_PLUGIN_H #define TRT_SPECIAL_SLICE_PLUGIN_H @@ -57,8 +56,8 @@ public: size_t getWorkspaceSize(int) const noexcept override; - int enqueue( - int batch_size, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) noexcept override; + int enqueue(int batch_size, const void* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept override; size_t getSerializationSize() const noexcept override; diff --git a/plugin/splitPlugin/split.cu b/plugin/splitPlugin/split.cu index d305ce7b..2b432b81 100644 --- a/plugin/splitPlugin/split.cu +++ b/plugin/splitPlugin/split.cu @@ -73,30 +73,31 @@ void split_kernel(int nsegment, } } -bool SplitPlugin::supportsFormatCombination(int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) +bool SplitPlugin::supportsFormatCombination( + int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept { - ASSERT(inOut && pos < (nbInputs + nbOutputs)); - return (inOut[pos].format == nvinfer1::PluginFormat::kNCHW); + ASSERT(inOut && pos < (nbInputs + nbOutputs)); + return (inOut[pos].format == nvinfer1::PluginFormat::kLINEAR); } -nvinfer1::DataType SplitPlugin::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const +nvinfer1::DataType SplitPlugin::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept { ASSERT(inputTypes && nbInputs > 0); return inputTypes[0]; } -int SplitPlugin::initialize() +int SplitPlugin::initialize() noexcept { return 0; } -void SplitPlugin::terminate() +void SplitPlugin::terminate() noexcept { } void SplitPlugin::configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, - const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) + const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) noexcept { std::vector segment_offsets(1, 0); for( int i = 0; i < nbOutputs; ++i ) @@ -130,9 +131,7 @@ void SplitPlugin::configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, i } int SplitPlugin::enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvinfer1::PluginTensorDesc* outputDesc, - const void* const* inputs, void* const* outputs, - void* workspace, - cudaStream_t stream) + const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { int const* d_segment_offsets_ptr = thrust::raw_pointer_cast(&_d_segment_offsets[0]); @@ -167,7 +166,7 @@ int SplitPlugin::enqueue(const nvinfer1::PluginTensorDesc* inputDesc, const nvin return cudaGetLastError() != cudaSuccess; } -nvinfer1::DimsExprs SplitPlugin::getOutputDimensions(int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) +nvinfer1::DimsExprs SplitPlugin::getOutputDimensions(int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) noexcept { nvinfer1::DimsExprs output(inputs[0]); output.d[_axis] = exprBuilder.constant(_output_lengths[outputIndex]); diff --git a/plugin/splitPlugin/split.h b/plugin/splitPlugin/split.h index 441bd108..4ed9eddf 100644 --- a/plugin/splitPlugin/split.h +++ b/plugin/splitPlugin/split.h @@ -18,8 +18,8 @@ #define TRT_SPLIT_PLUGIN_H #include -#include "checkMacrosPlugin.h" #include "serialize.hpp" +#include "checkMacrosPlugin.h" #include #include @@ -45,21 +45,16 @@ class SplitPlugin final : public nvinfer1::IPluginV2DynamicExt thrust::device_vector _d_output_ptrs; protected: - // Supress warnings about hiding function names due to overloads and overrides of virtuals. - using IPluginV2DynamicExt::enqueue; - using IPluginV2DynamicExt::getOutputDimensions; - using IPluginV2DynamicExt::getWorkspaceSize; - using IPluginV2DynamicExt::configurePlugin; - void deserialize(void const* serialData, size_t serialLength) + void deserialize(void const* serialData, size_t serialLength) noexcept { deserialize_value(&serialData, &serialLength, &_axis); deserialize_value(&serialData, &serialLength, &_output_lengths); } - size_t getSerializationSize() const override + size_t getSerializationSize() const noexcept override { return serialized_size(_axis) + serialized_size(_output_lengths); } - void serialize(void* buffer) const override + void serialize(void* buffer) const noexcept override { serialize_value(&buffer, _axis); serialize_value(&buffer, _output_lengths); @@ -83,53 +78,50 @@ public: this->deserialize(serialData, serialLength); } - bool supportsFormatCombination( - int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) override; - nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override; - int initialize() override; - void terminate() override; - void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, - const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) override; - int enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, const void* const* inputs, - void* const* outputs, void* workspace, cudaStream_t stream) TRTNOEXCEPT override; - nvinfer1::DimsExprs getOutputDimensions( - int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) override; + bool supportsFormatCombination(int pos, const nvinfer1::PluginTensorDesc* inOut, int nbInputs, int nbOutputs) noexcept override; + nvinfer1::DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const noexcept override; + int initialize() noexcept override; + void terminate() noexcept override; + void configurePlugin(const nvinfer1::DynamicPluginTensorDesc* in, int nbInputs, + const nvinfer1::DynamicPluginTensorDesc* out, int nbOutputs) noexcept override; + int enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; + nvinfer1::DimsExprs getOutputDimensions(int outputIndex, const nvinfer1::DimsExprs* inputs, int nbInputs, nvinfer1::IExprBuilder& exprBuilder) noexcept override; - nvinfer1::IPluginV2DynamicExt* clone() const override + nvinfer1::IPluginV2DynamicExt* clone() const noexcept override { return new SplitPlugin{_axis, _output_lengths}; } - void destroy() override + void destroy() noexcept override { delete this; } - const char* getPluginVersion() const override + const char* getPluginVersion() const noexcept override { return SPLIT_PLUGIN_VERSION; } - const char* getPluginType() const override + const char* getPluginType() const noexcept override { return SPLIT_PLUGIN_NAME; } size_t getWorkspaceSize(const nvinfer1::PluginTensorDesc* /*inputs*/, int /*nbInputs*/, - const nvinfer1::PluginTensorDesc* /*outputs*/, int /*nbOutputs*/) const TRTNOEXCEPT override + const nvinfer1::PluginTensorDesc* /*outputs*/, int /*nbOutputs*/) const noexcept override { return 0; } - void setPluginNamespace(const char* /*pluginNamespace*/) override {} - const char* getPluginNamespace() const override + void setPluginNamespace(const char* /*pluginNamespace*/) noexcept override {} + const char* getPluginNamespace() const noexcept override { return ""; } - int getNbOutputs() const override + int getNbOutputs() const noexcept override { return _output_lengths.size(); } void attachToContext( - cudnnContext* /*cudnn*/, cublasContext* /*cublas*/, nvinfer1::IGpuAllocator* /*allocator*/) override + cudnnContext* /*cudnn*/, cublasContext* /*cublas*/, nvinfer1::IGpuAllocator* /*allocator*/) noexcept override { } - void detachFromContext() override {} + void detachFromContext() noexcept override {} }; class SplitPluginCreator : public nvinfer1::IPluginCreator @@ -139,39 +131,39 @@ public: ~SplitPluginCreator() {} - const char* getPluginName() const + const char* getPluginName() const noexcept { return SPLIT_PLUGIN_NAME; } - const char* getPluginVersion() const + const char* getPluginVersion() const noexcept { return SPLIT_PLUGIN_VERSION; } - const nvinfer1::PluginFieldCollection* getFieldNames() + const nvinfer1::PluginFieldCollection* getFieldNames() noexcept { std::cerr << "Function not implemented" << std::endl; return nullptr; } - nvinfer1::IPluginV2DynamicExt* createPlugin(const char* /*name*/, const nvinfer1::PluginFieldCollection* /*fc*/) + nvinfer1::IPluginV2DynamicExt* createPlugin(const char* /*name*/, const nvinfer1::PluginFieldCollection* /*fc*/) noexcept { std::cerr << "Function not implemented" << std::endl; return nullptr; } - nvinfer1::IPluginV2DynamicExt* deserializePlugin(const char* /*name*/, const void* serialData, size_t serialLength) + nvinfer1::IPluginV2DynamicExt* deserializePlugin(const char* /*name*/, const void* serialData, size_t serialLength) noexcept { return new SplitPlugin{serialData, serialLength}; } - void setPluginNamespace(const char* libNamespace) + void setPluginNamespace(const char* libNamespace) noexcept { mNamespace = libNamespace; } - const char* getPluginNamespace() const + const char* getPluginNamespace() const noexcept { return mNamespace.c_str(); } diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 6dd33b8c..ddac8636 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -32,58 +32,34 @@ endfunction() # -------- CMAKE OPTIONS -------- -# Need libs in their own folder. set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tensorrt/) -# Set C++11 as standard for the whole project -set(CMAKE_CXX_STANDARD 11) -# pybind11 defaults to c++14. -set(PYBIND11_CPP_STANDARD -std=c++11) +set(CPP_STANDARD 11 CACHE STRING "CPP Standard Version") +set(CMAKE_CXX_STANDARD ${CPP_STANDARD}) # This allows us to use TRT libs shipped with the wheel. set(CMAKE_SHARED_LINKER_FLAGS -Wl,-rpath=$ORIGIN) -# Bindings library. The module name MUST MATCH the module name specified in python/src/pyTensorRT.cpp set(PY_MODULE_NAME tensorrt) # -------- PATHS -------- set_ifndef(TENSORRT_ROOT ../) # Convert to an absolute path. -set_ifndef(ONNX_INC_DIR ${TENSORRT_ROOT}/parsers/onnx/) +set_ifndef(ONNX_INC_DIR ${TENSORRT_ROOT}/parsers/) set_ifndef(PYBIND11_DIR ${EXT_PATH}/pybind11/) # Source Files file(GLOB_RECURSE SOURCE_FILES src/*.cpp) # Find headers -find_path(PY_INCLUDE Python.h - HINTS ${EXT_PATH}/python${PYTHON_MAJOR_VERSION}.${PYTHON_MINOR_VERSION} - PATH_SUFFIXES include -) +find_path(PY_INCLUDE Python.h HINTS ${EXT_PATH}/python${PYTHON_MAJOR_VERSION}.${PYTHON_MINOR_VERSION} PATH_SUFFIXES include) -set(PY_TARGET_DIR ${TARGET_ARCHITECTURE}-linux-gnu) -if (${TARGET_ARCHITECTURE} STREQUAL ppc64le) - set(PY_TARGET_DIR powerpc64le-linux-gnu) -endif() - -find_path(PY_CONFIG_INCLUDE pyconfig.h - HINTS ${PY_INCLUDE} - PATH_SUFFIXES ${PY_TARGET_DIR}/python${PYTHON_MAJOR_VERSION}.${PYTHON_MINOR_VERSION} -) +set(PY_TARGET_DIR ${TARGET}-linux-gnu) +find_path(PY_CONFIG_INCLUDE pyconfig.h HINTS ${PY_INCLUDE} PATH_SUFFIXES ${PY_TARGET_DIR}/python${PYTHON_MAJOR_VERSION}.${PYTHON_MINOR_VERSION}) # -------- GLOBAL COMPILE OPTIONS -------- -# Add include directories -include_directories(${TENSORRT_ROOT}/include - ${PROJECT_SOURCE_DIR}/include - ${CUDA_INCLUDE_DIRS} - ${PROJECT_SOURCE_DIR}/docstrings - ${ONNX_INC_DIR} - ${PYBIND11_DIR}/include -) - -# And lib directories. +include_directories(${TENSORRT_ROOT}/include ${PROJECT_SOURCE_DIR}/include ${CUDA_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/docstrings ${ONNX_INC_DIR} ${PYBIND11_DIR}/include) link_directories(${TENSORRT_BUILD}) -# Enable link-time optimizations -set(CMAKE_CXX_FLAGS "-fvisibility=hidden -std=c++11 -flto -fno-fat-lto-objects -Wno-deprecated-declarations") +set(CMAKE_CXX_FLAGS "-fvisibility=hidden -std=c++${CPP_STANDARD} -Wno-deprecated-declarations") # -------- START BUILD PROCESS -------- diff --git a/python/README.md b/python/README.md index c3016da0..67391f5a 100644 --- a/python/README.md +++ b/python/README.md @@ -36,7 +36,7 @@ Use `build.sh` to generate the installable wheels for intended python version an Example: for python 3.8 `x86_64` wheel, ```bash cd $TRT_OSSPATH/python -PYTHON_MAJOR_VERSION=3 PYTHON_MINOR_VERSION=8 TARGET_ARCHITECTURE=x86_64 ./build.sh +PYTHON_MAJOR_VERSION=3 PYTHON_MINOR_VERSION=8 TARGET=x86_64 ./build.sh ``` ### Install the python wheel diff --git a/python/build.sh b/python/build.sh index 1bde0a9d..1a94a60f 100755 --- a/python/build.sh +++ b/python/build.sh @@ -16,7 +16,7 @@ PYTHON_MAJOR_VERSION=${PYTHON_MAJOR_VERSION:-3} PYTHON_MINOR_VERSION=${PYTHON_MINOR_VERSION:-8} -TARGET_ARCHITECTURE=${TARGET_ARCHITECTURE:-x86_64} +TARGET=${TARGET:-x86_64} ROOT_PATH=${TRT_OSSPATH:-/workspace/TensorRT} EXT_PATH=${EXT_PATH:-/tmp/external} WHEEL_OUTPUT_DIR=${ROOT_PATH}/python/build @@ -27,7 +27,7 @@ pushd ${WHEEL_OUTPUT_DIR} # Generate tensorrt.so echo $(ls ${ROOT_PATH}/python/include) cmake .. -DCMAKE_BUILD_TYPE=Release \ - -DTARGET_ARCHITECTURE=${TARGET_ARCHITECTURE} \ + -DTARGET=${TARGET} \ -DPYTHON_MAJOR_VERSION=${PYTHON_MAJOR_VERSION} \ -DPYTHON_MINOR_VERSION=${PYTHON_MINOR_VERSION} \ -DEXT_PATH=${EXT_PATH} \ @@ -57,6 +57,6 @@ pushd ${ROOT_PATH}/python/packaging for dir in $(find . -type d); do mkdir -p ${WHEEL_OUTPUT_DIR}/$dir; done for file in $(find . -type f); do expand_vars_cp $file ${WHEEL_OUTPUT_DIR}/${file}; done popd -python3 setup.py -q bdist_wheel --python-tag=cp${PYTHON_MAJOR_VERSION}${PYTHON_MINOR_VERSION} --plat-name=linux_${TARGET_ARCHITECTURE} +python3 setup.py -q bdist_wheel --python-tag=cp${PYTHON_MAJOR_VERSION}${PYTHON_MINOR_VERSION} --plat-name=linux_${TARGET} popd diff --git a/python/docstrings/infer/pyAlgorithmSelectorDoc.h b/python/docstrings/infer/pyAlgorithmSelectorDoc.h index 36a23522..55062d8c 100644 --- a/python/docstrings/infer/pyAlgorithmSelectorDoc.h +++ b/python/docstrings/infer/pyAlgorithmSelectorDoc.h @@ -19,122 +19,123 @@ namespace tensorrt { - namespace IAlgorithmIOInfoDOC - { - constexpr const char* descr = R"trtdoc( - This class carries information about input or output of the algorithm. - IAlgorithmIOInfo for all the input and output along with IAlgorithmVariant denotes the variation of algorithm - and can be used to select or reproduce an algorithm using IAlgorithmSelector::selectAlgorithms(). +namespace IAlgorithmIOInfoDOC +{ +constexpr const char* descr = R"trtdoc( + This class carries information about input or output of the algorithm. + IAlgorithmIOInfo for all the input and output along with IAlgorithmVariant denotes the variation of algorithm + and can be used to select or reproduce an algorithm using IAlgorithmSelector.select_algorithms(). - :ivar tensor_format: :class:`TensorFormat` TensorFormat of the input/output of algorithm. - :ivar dtype: :class:`DataType` DataType of the input/output of algorithm. - :ivar strides: :class:`Dims` strides of the input/output tensor of algorithm. - )trtdoc"; - } /* IAlgorithmIOInfoDOC */ + :ivar tensor_format: :class:`TensorFormat` TensorFormat of the input/output of algorithm. + :ivar dtype: :class:`DataType` DataType of the input/output of algorithm. + :ivar strides: :class:`Dims` strides of the input/output tensor of algorithm. +)trtdoc"; +} // namespace IAlgorithmIOInfoDOC - namespace IAlgorithmVariantDOC - { - constexpr const char* descr = R"trtdoc( - provides a unique 128-bit identifier, which along with the input and output information - denotes the variation of algorithm and can be used to select or reproduce an algorithm, - using IAlgorithmSelector::selectAlgorithms() - see IAlgorithmIOInfo, IAlgorithm, IAlgorithmSelector::selectAlgorithms() - note A single implementation can have multiple tactics. +namespace IAlgorithmVariantDOC +{ +constexpr const char* descr = R"trtdoc( + provides a unique 128-bit identifier, which along with the input and output information + denotes the variation of algorithm and can be used to select or reproduce an algorithm, + using IAlgorithmSelector.select_algorithms() + see IAlgorithmIOInfo, IAlgorithm, IAlgorithmSelector.select_algorithms() + note A single implementation can have multiple tactics. - :ivar implementation: :class:`int` implementation of the algorithm. - :ivar tactic: :class:`int` tactic of the algorithm. - )trtdoc"; + :ivar implementation: :class:`int` implementation of the algorithm. + :ivar tactic: :class:`int` tactic of the algorithm. +)trtdoc"; - } /* IAlgorithmVariantDOC*/ +} // namespace IAlgorithmVariantDOC - namespace IAlgorithmContextDoc - { - constexpr const char* descr = R"trtdoc( - Describes the context and requirements, that could be fulfilled by one or - more instances of IAlgorithm. - see IAlgorithm +namespace IAlgorithmContextDoc +{ +constexpr const char* descr = R"trtdoc( + Describes the context and requirements, that could be fulfilled by one or + more instances of IAlgorithm. + see IAlgorithm - :ivar name: :class:`str` name of the algorithm node. - :ivar num_inputs: :class:`int` number of inputs of the algorithm. - :ivar num_outputs: :class:`int` number of outputs of the algorithm. - )trtdoc"; + :ivar name: :class:`str` name of the algorithm node. + :ivar num_inputs: :class:`int` number of inputs of the algorithm. + :ivar num_outputs: :class:`int` number of outputs of the algorithm. +)trtdoc"; - constexpr const char* get_shape = R"trtdoc( - Get the minimum / optimum / maximum dimensions for a dynamic input tensor. - choices --> (min, max, opt) +constexpr const char* get_shape = R"trtdoc( + Get the minimum / optimum / maximum dimensions for a dynamic input tensor. + choices --> (min, max, opt) - :arg index: Index of the input or output of the algorithm. Incremental numbers assigned to indices of inputs and the outputs. + :arg index: Index of the input or output of the algorithm. Incremental numbers assigned to indices of inputs and the outputs. - :returns: A `List[Dims]` of length 3, containing the minimum, optimum, and maximum shapes, in that order. If the shapes have not been set yet, an empty list is returned.` - )trtdoc"; - } /* IAlgorithmContextDoc*/ + :returns: A `List[Dims]` of length 3, containing the minimum, optimum, and maximum shapes, in that order. If the shapes have not been set yet, an empty list is returned.` +)trtdoc"; +} // namespace IAlgorithmContextDoc - namespace IAlgorithmDoc - { - constexpr const char* descr = R"trtdoc( - Application-implemented interface for selecting and reporting the tactic selection of a layer. - Tactic Selection is a step performed by the builder for deciding best algorithms for a layer. +namespace IAlgorithmDoc +{ +constexpr const char* descr = R"trtdoc( + Application-implemented interface for selecting and reporting the tactic selection of a layer. + Tactic Selection is a step performed by the builder for deciding best algorithms for a layer. - :ivar algorithm_variant: :class:`IAlgorithmVariant&` the algorithm variant. - :ivar timing_msec: :class:`float` The time in milliseconds to execute the algorithm. - :ivar workspace_size: :class:`int` The size of the GPU temporary memory in bytes which the algorithm uses at execution time. - )trtdoc"; + :ivar algorithm_variant: :class:`IAlgorithmVariant&` the algorithm variant. + :ivar timing_msec: :class:`float` The time in milliseconds to execute the algorithm. + :ivar workspace_size: :class:`int` The size of the GPU temporary memory in bytes which the algorithm uses at execution time. +)trtdoc"; - constexpr const char* get_algorithm_io_info = R"trtdoc( - A single call for both inputs and outputs. Incremental numbers assigned to indices of inputs and the outputs. +constexpr const char* get_algorithm_io_info = R"trtdoc( + A single call for both inputs and outputs. Incremental numbers assigned to indices of inputs and the outputs. - :arg index: Index of the input or output of the algorithm. Incremental numbers assigned to indices of inputs and the outputs. + :arg index: Index of the input or output of the algorithm. Incremental numbers assigned to indices of inputs and the outputs. - :returns: A :class:`IAlgorithmIOInfo&` - )trtdoc"; - } /* IAlgorithmDoc */ + :returns: A :class:`IAlgorithmIOInfo&` +)trtdoc"; +} // namespace IAlgorithmDoc - namespace IAlgorithmSelectorDoc - { - constexpr const char* descr = R"trtdoc( - Interface implemented by application for selecting and reporting algorithms of a layer provided by the - builder. - note A layer in context of algorithm selection may be different from ILayer in INetworkDefiniton. - For example, an algorithm might be implementing a conglomeration of multiple ILayers in INetworkDefinition. - )trtdoc"; +namespace IAlgorithmSelectorDoc +{ +constexpr const char* descr = R"trtdoc( + Interface implemented by application for selecting and reporting algorithms of a layer provided by the + builder. + note A layer in context of algorithm selection may be different from ILayer in INetworkDefiniton. + For example, an algorithm might be implementing a conglomeration of multiple ILayers in INetworkDefinition. +)trtdoc"; - constexpr const char* select_algorithms = R"trtdoc( - Select Algorithms for a layer from the given list of algorithm choices. - return The number of choices selected from [0, len(choices)-1]. - note TRT uses its default algorithm selection to choose from the list provided. - If return value is 0, TRT’s default algorithm selection is used unless strict type constraints are set. - The list of choices is valid only for this specific algorithm context. +constexpr const char* select_algorithms = R"trtdoc( + Select Algorithms for a layer from the given list of algorithm choices. - A possible implementation may look like this: - :: - def select_algorithms(self, context, choices): - assert len(choices) > 0 - selection = [i for i in range(len(choices))] - return (len(choices), selection) + Note: TRT uses its default algorithm selection to choose from the list returned by the user. + If the returned list is empty, TRT’s default algorithm selection is used unless strict type constraints are set. + The list of choices is valid only for this specific algorithm context. - :arg context: The context for which the algorithm choices are valid. - :arg choices: The list of algorithm choices to select for implementation of this layer. - :arg selection: The user writes indices of selected choices in to selection buffer which is of size number of choices. + A possible implementation may look like this: + :: - :returns: A :class:`Tuple(int, List[int])` this first values in the size of the array the second one is a sublist of tactic indices from selection. + def select_algorithms(self, context, choices): + assert len(choices) > 0 + return list(range(len(choices))) - )trtdoc"; - - constexpr const char* report_algorithm = R"trtdoc( - Called by TensorRT to report choices it made. - - note For a given optimization profile, this call comes after all calls to selectAlgorithms. - choices[i] is the choice that TensorRT made for algoContexts[i], for i in [0, num_algorithms-1] + :arg context: The context for which the algorithm choices are valid. + :arg choices: The list of algorithm choices to select for implementation of this layer. - A possible implementation may look like this: - :: - def report_algorithms(self, contexts, choices): - # Prints the time of the chosen algorithm by TRT from the - # selection list passed in by select_algorithms - print(algoChoices[0].timing_msec) - - :arg contexts: The list of all algorithm contexts. - :arg choices: The list of algorithm choices made by TensorRT. - )trtdoc"; - } /* IAlgorithmSelectorDoc */ -} + :returns: A :class:`List[int]` indicating the indices from the choices vector that TensorRT should choose from. + +)trtdoc"; + +constexpr const char* report_algorithms = R"trtdoc( + Called by TensorRT to report choices it made. + + Note: For a given optimization profile, this call comes after all calls to select_algorithms. + choices[i] is the choice that TensorRT made for algoContexts[i], for i in [0, num_algorithms-1] + + A possible implementation may look like this: + :: + + def report_algorithms(self, contexts, choices): + # Prints the time of the chosen algorithm by TRT from the + # selection list passed in by select_algorithms + for choice in choices: + print(choice.timing_msec) + + :arg contexts: The list of all algorithm contexts. + :arg choices: The list of algorithm choices made by TensorRT corresponding to each context. +)trtdoc"; +} // namespace IAlgorithmSelectorDoc +} // namespace tensorrt diff --git a/python/docstrings/infer/pyCoreDoc.h b/python/docstrings/infer/pyCoreDoc.h index 931376f7..abc78241 100644 --- a/python/docstrings/infer/pyCoreDoc.h +++ b/python/docstrings/infer/pyCoreDoc.h @@ -19,1077 +19,1369 @@ namespace tensorrt { - namespace LoggerDoc - { - constexpr const char* descr = R"trtdoc( - Logger for the :class:`Builder`, :class:`ICudaEngine` and :class:`Runtime` . - :arg min_severity: The initial minimum severity of this Logger. +namespace ILoggerDoc +{ +constexpr const char* descr = R"trtdoc( +Abstract base Logger class for the :class:`Builder`, :class:`ICudaEngine` and :class:`Runtime` . - :ivar min_severity: :class:`Logger.Severity` This minimum required severity of messages for the logger to log them. +To implement a custom logger, ensure that you explicitly instantiate the base class in :func:`__init__` : +:: - Note that although a logger is passed on creation to each instance of a :class:`Builder` or :class:`Runtime` interface, the logger is internally considered a singleton, and thus - multiple instances of :class:`Runtime` and/or :class:`Builder` must all use the same logger. - )trtdoc"; + class MyLogger(trt.ILogger): + def __init__(self): + trt.ILogger.__init__(self) - constexpr const char* log = R"trtdoc( - Logs a message to `stderr` . + def log(self, severity, msg): + ... # Your implementation here - :arg severity: The severity of the message. - :arg msg: The log message. - Derived classes should generally overload this function. - )trtdoc"; - } /* LoggerDoc */ +:arg min_severity: The initial minimum severity of this Logger. - namespace SeverityDoc - { - constexpr const char* internal_error = R"trtdoc( - Represents an internal error. Execution is unrecoverable. - )trtdoc"; +:ivar min_severity: :class:`Logger.Severity` This minimum required severity of messages for the logger to log them. - constexpr const char* error = R"trtdoc( - Represents an application error. - )trtdoc"; +Note that although a logger is passed on creation to each instance of a :class:`Builder` or :class:`Runtime` interface, the logger is internally considered a singleton, and thus +multiple instances of :class:`Runtime` and/or :class:`Builder` must all use the same logger. +)trtdoc"; - constexpr const char* warning = R"trtdoc( - Represents an application error that TensorRT has recovered from or fallen back to a default. - )trtdoc"; +constexpr const char* log = R"trtdoc( +Logs a message to `stderr` . This function must be overriden by a derived class. - constexpr const char* info = R"trtdoc( - Represents informational messages. - )trtdoc"; +:arg severity: The severity of the message. +:arg msg: The log message. - constexpr const char* verbose = R"trtdoc( - Verbose messages with debugging information. - )trtdoc"; - } /* SeverityDoc */ +)trtdoc"; +} // namespace ILoggerDoc - namespace ProfilerDoc - { - constexpr const char* descr = R"trtdoc( - When this class is added to an :class:`IExecutionContext`, the profiler will be called once per layer for each invocation of :func:`IExecutionContext.execute()` . - Note that :func:`IExecutionContext.execute_async()` does not currently support profiling. +namespace LoggerDoc +{ +constexpr const char* descr = R"trtdoc( +Logger for the :class:`Builder`, :class:`ICudaEngine` and :class:`Runtime` . - The profiler will only be called after execution is complete. It has a small impact on execution time. - )trtdoc"; +:arg min_severity: The initial minimum severity of this Logger. - constexpr const char* report_layer_time = R"trtdoc( - Reports time in milliseconds for each layer. This function should be overloaded by classes derived from IProfiler. +:ivar min_severity: :class:`Logger.Severity` This minimum required severity of messages for the logger to log them. - :arg layer_name: The name of the layer, set when constructing the :class:`INetworkDefinition` . - :arg ms: The time in milliseconds to execute the layer. - )trtdoc"; - } /* ProfilerDoc */ +Note that although a logger is passed on creation to each instance of a :class:`Builder` or :class:`Runtime` interface, the logger is internally considered a singleton, and thus +multiple instances of :class:`Runtime` and/or :class:`Builder` must all use the same logger. +)trtdoc"; - namespace IOptimizationProfileDoc - { - constexpr const char* descr = R"trtdoc( - Optimization profile for dynamic input dimensions and shape tensors. +constexpr const char* log = R"trtdoc( +Logs a message to `stderr` . - When building an :class:`ICudaEngine` from an :class:`INetworkDefinition` that has dynamically resizable inputs (at least - one input tensor has one or more of its dimensions specified as -1) or shape input tensors, users need to specify - at least one optimization profile. Optimization profiles are numbered 0, 1, ... +:arg severity: The severity of the message. +:arg msg: The log message. +)trtdoc"; +} // namespace LoggerDoc - The first optimization profile that has been defined (with index 0) will be used by the :class:`ICudaEngine` whenever no - optimization profile has been selected explicitly. If none of the inputs are dynamic, the default optimization - profile will be generated automatically unless it is explicitly provided by the user (this is possible but not - required in this case). If more than a single optimization profile is defined, users may set a target how - much additional weight space should be maximally allocated to each additional profile (as a fraction of the - maximum, unconstrained memory). +namespace SeverityDoc +{ +constexpr const char* internal_error = R"trtdoc( + Represents an internal error. Execution is unrecoverable. +)trtdoc"; - Users set optimum input tensor dimensions, as well as minimum and maximum input tensor dimensions. The builder - selects the kernels that result in the lowest runtime for the optimum input tensor dimensions, and are valid for - all input tensor sizes in the valid range between minimum and maximum dimensions. A runtime error will be raised - if the input tensor dimensions fall outside the valid range for this profile. Likewise, users provide minimum, - optimum, and maximum values for all shape tensor input values. +constexpr const char* error = R"trtdoc( + Represents an application error. +)trtdoc"; - :class:`IOptimizationProfile` implements :func:`__nonzero__` and :func:`__bool__` such that evaluating a profile as a :class:`bool` (e.g. ``if profile:``) will check whether the optimization profile can be passed to an IBuilderConfig object. This will perform partial validation, by e.g. checking that the maximum dimensions are at least as large as the optimum dimensions, and that the optimum dimensions are always as least as large as the minimum dimensions. Some validation steps require knowledge of the network definition and are deferred to engine build time. +constexpr const char* warning = R"trtdoc( + Represents an application error that TensorRT has recovered from or fallen back to a default. +)trtdoc"; - :ivar extra_memory_target: Additional memory that the builder should aim to maximally allocate for this profile, as a fraction of the memory it would use if the user did not impose any constraints on memory. This unconstrained case is the default; it corresponds to ``extra_memory_target`` == 1.0. If ``extra_memory_target`` == 0.0, the builder aims to create the new optimization profile without allocating any additional weight memory. Valid inputs lie between 0.0 and 1.0. This parameter is only a hint, and TensorRT does not guarantee that the ``extra_memory_target`` will be reached. This parameter is ignored for the first (default) optimization profile that is defined. - )trtdoc"; - - constexpr const char* set_shape = R"trtdoc( - Set the minimum/optimum/maximum dimensions for a dynamic input tensor. - - This function must be called for any network input tensor that has dynamic dimensions. If ``min``, ``opt``, and ``max`` are the minimum, optimum, and maximum dimensions, and ``real_shape`` is the shape for this input tensor provided to the :class:`INetworkDefinition` ,then the following conditions must hold: - - (1) ``len(min)`` == ``len(opt)`` == ``len(max)`` == ``len(real_shape)`` - (2) 1 <= ``min[i]`` <= ``opt[i]`` <= ``max[i]`` for all ``i`` - (3) if ``real_shape[i]`` != -1, then ``min[i]`` == ``opt[i]`` == ``max[i]`` == ``real_shape[i]`` - - This function may (but need not be) called for an input tensor that does not have dynamic dimensions. In this - case, all shapes must equal ``real_shape``. - - :arg input: The name of the input tensor. - :arg min: The minimum dimensions for this input tensor. - :arg opt: The optimum dimensions for this input tensor. - :arg max: The maximum dimensions for this input tensor. - - :raises: :class:`ValueError` if an inconsistency was detected. Note that inputs can be validated only partially; a full validation is performed at engine build time. - )trtdoc"; - - constexpr const char* get_shape = R"trtdoc( - Get the minimum/optimum/maximum dimensions for a dynamic input tensor. - If the dimensions have not been previously set via :func:`set_shape`, return an invalid :class:`Dims` with a length of -1. - - :returns: A ``List[Dims]`` of length 3, containing the minimum, optimum, and maximum shapes, in that order. If the shapes have not been set yet, an empty list is returned. - )trtdoc"; - - constexpr const char* set_shape_input = R"trtdoc( - Set the minimum/optimum/maximum values for a shape input tensor. - - This function must be called for every input tensor ``t`` that is a shape tensor (``t.is_shape`` == ``True``). - This implies that the datatype of ``t`` is ``int32``, the rank is either 0 or 1, and the dimensions of ``t`` - are fixed at network definition time. This function must NOT be called for any input tensor that is not a - shape tensor. - - If ``min``, ``opt``, and ``max`` are the minimum, optimum, and maximum values, it must be true that ``min[i]`` <= ``opt[i]`` <= ``max[i]`` for - all ``i``. - - :arg input: The name of the input tensor. - :arg min: The minimum values for this shape tensor. - :arg opt: The optimum values for this shape tensor. - :arg max: The maximum values for this shape tensor. - - :raises: :class:`ValueError` if an inconsistency was detected. Note that inputs can be validated only partially; a full validation is performed at engine build time. - )trtdoc"; - - constexpr const char* get_shape_input = R"trtdoc( - Get the minimum/optimum/maximum values for a shape input tensor. - - :returns: A ``List[List[int]]`` of length 3, containing the minimum, optimum, and maximum values, in that order. If the values have not been set yet, an empty list is returned. - )trtdoc"; - } // IOptimizationProfileDoc - - namespace ErrorCodeDoc - { - constexpr const char* descr = R"trtdoc(Error codes that can be returned by TensorRT during execution.)trtdoc"; - - constexpr const char* SUCCESS = R"trtdoc(Execution completed successfully.)trtdoc"; - - constexpr const char* UNSPECIFIED_ERROR = R"trtdoc( - An error that does not fall into any other category. This error is included for forward compatibility. - )trtdoc"; - - constexpr const char* INTERNAL_ERROR = R"trtdoc(A non-recoverable TensorRT error occurred.)trtdoc"; - - constexpr const char* INVALID_ARGUMENT = R"trtdoc( - An argument passed to the function is invalid in isolation. This is a violation of the API contract. - )trtdoc"; - - constexpr const char* INVALID_CONFIG = R"trtdoc( - An error occurred when comparing the state of an argument relative to other arguments. For example, the - dimensions for concat differ between two tensors outside of the channel dimension. This error is triggered - when an argument is correct in isolation, but not relative to other arguments. This is to help to distinguish - from the simple errors from the more complex errors. - This is a violation of the API contract. - )trtdoc"; - - constexpr const char* FAILED_ALLOCATION = R"trtdoc( - An error occurred when performing an allocation of memory on the host or the device. - A memory allocation error is normally fatal, but in the case where the application provided its own memory - allocation routine, it is possible to increase the pool of available memory and resume execution. - )trtdoc"; - - constexpr const char* FAILED_INITIALIZATION = R"trtdoc( - One, or more, of the components that TensorRT relies on did not initialize correctly. - This is a system setup issue. - )trtdoc"; - - constexpr const char* FAILED_EXECUTION = R"trtdoc( - An error occurred during execution that caused TensorRT to end prematurely, either an asynchronous error or - other execution errors reported by CUDA/DLA. In a dynamic system, the - data can be thrown away and the next frame can be processed or execution can be retried. - This is either an execution error or a memory error. - )trtdoc"; - - constexpr const char* FAILED_COMPUTATION = R"trtdoc( - An error occurred during execution that caused the data to become corrupted, but execution finished. Examples - of this error are NaN squashing or integer overflow. In a dynamic system, the data can be thrown away and the - next frame can be processed or execution can be retried. - This is either a data corruption error, an input error, or a range error. - )trtdoc"; - - constexpr const char* INVALID_STATE = R"trtdoc( - TensorRT was put into a bad state by incorrect sequence of function calls. An example of an invalid state is - specifying a layer to be DLA only without GPU fallback, and that layer is not supported by DLA. This can occur - in situations where a service is optimistically executing networks for multiple different configurations - without checking proper error configurations, and instead throwing away bad configurations caught by TensorRT. - This is a violation of the API contract, but can be recoverable. - - Example of a recovery: - GPU fallback is disabled and conv layer with large filter(63x63) is specified to run on DLA. This will fail due - to DLA not supporting the large kernel size. This can be recovered by either turning on GPU fallback - or setting the layer to run on the GPU. - )trtdoc"; - - constexpr const char* UNSUPPORTED_STATE = R"trtdoc( - An error occurred due to the network not being supported on the device due to constraints of the hardware or - system. An example is running a unsafe layer in a safety certified context, or a resource requirement for the - current network is greater than the capabilities of the target device. The network is otherwise correct, but - the network and hardware combination is problematic. This can be recoverable. - Examples: - * Scratch space requests larger than available device memory and can be recovered by increasing allowed workspace size. - * Tensor size exceeds the maximum element count and can be recovered by reducing the maximum batch size. - )trtdoc"; - } // ErrorCodeDoc - - namespace IErrorRecorderDoc - { - constexpr const char* descr = R"trtdoc( - Reference counted application-implemented error reporting interface for TensorRT objects. - - The error reporting mechanism is a user defined object that interacts with the internal state of the object - that it is assigned to in order to determine information about abnormalities in execution. The error recorder - gets both an error enum that is more descriptive than pass/fail and also a description that gives more - detail on the exact failure modes. In the safety context, the error strings are all limited to 128 characters - in length. - The ErrorRecorder gets passed along to any class that is created from another class that has an ErrorRecorder - assigned to it. For example, assigning an ErrorRecorder to an IBuilder allows all INetwork's, ILayer's, and - ITensor's to use the same error recorder. For functions that have their own ErrorRecorder accessor functions. - This allows registering a different error recorder or de-registering of the error recorder for that specific - object. - - The ErrorRecorder object implementation must be thread safe if the same ErrorRecorder is passed to different - interface objects being executed in parallel in different threads. All locking and synchronization is - pushed to the interface implementation and TensorRT does not hold any synchronization primitives when accessing - the interface functions. - )trtdoc"; - - constexpr const char* has_overflowed = R"trtdoc( - Determine if the error stack has overflowed. - - In the case when the number of errors is large, this function is used to query if one or more - errors have been dropped due to lack of storage capacity. This is especially important in the - automotive safety case where the internal error handling mechanisms cannot allocate memory. - - :returns: True if errors have been dropped due to overflowing the error stack. - )trtdoc"; - - constexpr const char* get_num_errors = R"trtdoc( - Return the number of errors - - Determines the number of errors that occurred between the current point in execution - and the last time that the clear() was executed. Due to the possibility of asynchronous - errors occuring, a TensorRT API can return correct results, but still register errors - with the Error Recorder. The value of getNbErrors must monotonically increases until clear() - is called. - - :returns: Returns the number of errors detected, or 0 if there are no errors. - )trtdoc"; - - constexpr const char* get_error_code = R"trtdoc( - Returns the ErrorCode enumeration. - - The error_idx specifies what error code from 0 to :attr:`num_errors`-1 that the application - wants to analyze and return the error code enum. - - :arg error_idx: A 32bit integer that indexes into the error array. - - :returns: Returns the enum corresponding to error_idx. - )trtdoc"; - - constexpr const char* get_error_desc = R"trtdoc( - Returns description of the error. - - For the error specified by the idx value, return description of the error. In the safety context there is a - constant length requirement to remove any dynamic memory allocations and the error message - may be truncated. The format of the error description is " - ". - - :arg error_idx: A 32bit integer that indexes into the error array. - - :returns: Returns description of the error. - )trtdoc"; - - constexpr const char* clear = R"trtdoc( - Clear the error stack on the error recorder. - - Removes all the tracked errors by the error recorder. This function must guarantee that after - this function is called, and as long as no error occurs, :attr:`num_errors` will be zero. - )trtdoc"; - - constexpr const char* report_error = R"trtdoc( - Clear the error stack on the error recorder. - - Report an error to the user that has a given value and human readable description. The function returns false - if processing can continue, which implies that the reported error is not fatal. This does not guarantee that - processing continues, but provides a hint to TensorRT. - - :arg val: The error code enum that is being reported. - :arg desc: The description of the error. - - :returns: True if the error is determined to be fatal and processing of the current function must end. - )trtdoc"; - } // IErrorRecorderDoc - - namespace IExecutionContextDoc - { - constexpr const char* descr = R"trtdoc( - Context for executing inference using an :class:`ICudaEngine` . - Multiple :class:`IExecutionContext` s may exist for one :class:`ICudaEngine` instance, allowing the same - :class:`ICudaEngine` to be used for the execution of multiple batches simultaneously. +constexpr const char* info = R"trtdoc( + Represents informational messages. +)trtdoc"; - :ivar debug_sync: :class:`bool` The debug sync flag. If this flag is set to true, the :class:`ICudaEngine` will log the successful execution for each kernel during execute(). It has no effect when using execute_async(). - :ivar profiler: :class:`IProfiler` The profiler in use by this :class:`IExecutionContext` . - :ivar engine: :class:`ICudaEngine` The associated :class:`ICudaEngine` . - :ivar name: :class:`str` The name of the :class:`IExecutionContext` . - :ivar device_memory: :class:`capsule` The device memory for use by this execution context. The memory must be aligned on a 256-byte boundary, and its size must be at least :attr:`engine.device_memory_size`. If using :func:`execute_async` to run the network, The memory is in use from the invocation of :func:`execute_async` until network execution is complete. If using :func:`execute`, it is in use until :func:`execute` returns. Releasing or otherwise using the memory for other purposes during this time will result in undefined behavior. - :ivar active_optimization_profile: :class:`int` The active optimization profile for the context. The selected profile will be used in subsequent calls to :func:`execute` or :func:`execute_async` . Profile 0 is selected by default. Changing this value will invalidate all dynamic bindings for the current execution context, so that they have to be set again using :func:`set_binding_shape` before calling either :func:`execute` or :func:`execute_async` . - :ivar all_binding_shapes_specified: :class:`bool` Whether all dynamic dimensions of input tensors have been specified by calling :func:`set_binding_shape` . Trivially true if network has no dynamically shaped input tensors. - :ivar all_shape_inputs_specified: :class:`bool` Whether values for all input shape tensors have been specified by calling :func:`set_shape_input` . Trivially true if network has no input shape bindings. - )trtdoc"; +constexpr const char* verbose = R"trtdoc( + Verbose messages with debugging information. +)trtdoc"; +} // namespace SeverityDoc - constexpr const char* execute = R"trtdoc( - Synchronously execute inference on a batch. - This method requires a array of input and output buffers. The mapping from tensor names to indices can be queried using :func:`ICudaEngine.get_binding_index()` . +namespace IProfilerDoc +{ +constexpr const char* descr = R"trtdoc( + Abstract base Profiler class. - :arg batch_size: The batch size. This is at most the value supplied when the :class:`ICudaEngine` was built. - :arg bindings: A list of integers representing input and output buffer addresses for the network. + To implement a custom profiler, ensure that you explicitly instantiate the base class in :func:`__init__` : + :: - :returns: True if execution succeeded. - )trtdoc"; + class MyProfiler(trt.IProfiler): + def __init__(self): + trt.IProfiler.__init__(self) - constexpr const char* execute_async = R"trtdoc( - Asynchronously execute inference on a batch. - This method requires a array of input and output buffers. The mapping from tensor names to indices can be queried using :func:`ICudaEngine::get_binding_index()` . + def report_layer_time(self, layer_name, ms): + ... # Your implementation here - :arg batch_size: The batch size. This is at most the value supplied when the :class:`ICudaEngine` was built. - :arg bindings: A list of integers representing input and output buffer addresses for the network. - :arg stream_handle: A handle for a CUDA stream on which the inference kernels will be executed. - :arg input_consumed: An optional event which will be signaled when the input buffers can be refilled with new data + When this class is added to an :class:`IExecutionContext`, the profiler will be called once per layer for each invocation of :func:`IExecutionContext.execute()` . + Note that :func:`IExecutionContext.execute_async()` does not currently support profiling. - :returns: True if the kernels were executed successfully. - )trtdoc"; + The profiler will only be called after execution is complete. It has a small impact on execution time. +)trtdoc"; - constexpr const char* execute_v2 = R"trtdoc( - Synchronously execute inference on a batch. - This method requires a array of input and output buffers. The mapping from tensor names to indices can be queried using :func:`ICudaEngine.get_binding_index()` . - This method only works for execution contexts built from networks with no implicit batch dimension. +constexpr const char* report_layer_time = R"trtdoc( + Reports time in milliseconds for each layer. This function must be overriden a derived class. - :arg bindings: A list of integers representing input and output buffer addresses for the network. + :arg layer_name: The name of the layer, set when constructing the :class:`INetworkDefinition` . + :arg ms: The time in milliseconds to execute the layer. +)trtdoc"; +} // namespace IProfilerDoc - :returns: True if execution succeeded. - )trtdoc"; +namespace ProfilerDoc +{ +constexpr const char* descr = R"trtdoc( + When this class is added to an :class:`IExecutionContext`, the profiler will be called once per layer for each invocation of :func:`IExecutionContext.execute()` . + Note that :func:`IExecutionContext.execute_async()` does not currently support profiling. - constexpr const char* execute_async_v2 = R"trtdoc( - Asynchronously execute inference on a batch. - This method requires a array of input and output buffers. The mapping from tensor names to indices can be queried using :func:`ICudaEngine::get_binding_index()` . - This method only works for execution contexts built from networks with no implicit batch dimension. + The profiler will only be called after execution is complete. It has a small impact on execution time. +)trtdoc"; - :arg bindings: A list of integers representing input and output buffer addresses for the network. - :arg stream_handle: A handle for a CUDA stream on which the inference kernels will be executed. - :arg input_consumed: An optional event which will be signaled when the input buffers can be refilled with new data +constexpr const char* report_layer_time = R"trtdoc( + Prints time in milliseconds for each layer to stdout. - :returns: True if the kernels were executed successfully. - )trtdoc"; + :arg layer_name: The name of the layer, set when constructing the :class:`INetworkDefinition` . + :arg ms: The time in milliseconds to execute the layer. +)trtdoc"; +} // namespace ProfilerDoc - constexpr const char* device_memory = R"trtdoc( - The device memory for use by this :class:`IExecutionContext` . +namespace IOptimizationProfileDoc +{ +constexpr const char* descr = R"trtdoc( + Optimization profile for dynamic input dimensions and shape tensors. - The memory must be aligned on a 256-byte boundary, and its size must be at least that - returned by getDeviceMemorySize(). If using :func:`execute_async()` to run the network, The memory is in - use from the invocation of :func:`execute_async()` until network execution is complete. If using :func:`execute()`, - it is in use until :func:`execute()` returns. Releasing or otherwise using the memory for other - purposes during this time will result in undefined behavior. - )trtdoc"; + When building an :class:`ICudaEngine` from an :class:`INetworkDefinition` that has dynamically resizable inputs (at least + one input tensor has one or more of its dimensions specified as -1) or shape input tensors, users need to specify + at least one optimization profile. Optimization profiles are numbered 0, 1, ... - constexpr const char* get_strides = R"trtdoc( - Return the strides of the buffer for the given binding. + The first optimization profile that has been defined (with index 0) will be used by the :class:`ICudaEngine` whenever no + optimization profile has been selected explicitly. If none of the inputs are dynamic, the default optimization + profile will be generated automatically unless it is explicitly provided by the user (this is possible but not + required in this case). If more than a single optimization profile is defined, users may set a target how + much additional weight space should be maximally allocated to each additional profile (as a fraction of the + maximum, unconstrained memory). - Note that strides can be different for different execution contexts with dynamic shapes. + Users set optimum input tensor dimensions, as well as minimum and maximum input tensor dimensions. The builder + selects the kernels that result in the lowest runtime for the optimum input tensor dimensions, and are valid for + all input tensor sizes in the valid range between minimum and maximum dimensions. A runtime error will be raised + if the input tensor dimensions fall outside the valid range for this profile. Likewise, users provide minimum, + optimum, and maximum values for all shape tensor input values. - :arg binding: The binding index. - )trtdoc"; + :class:`IOptimizationProfile` implements :func:`__nonzero__` and :func:`__bool__` such that evaluating a profile as a :class:`bool` (e.g. ``if profile:``) will check whether the optimization profile can be passed to an IBuilderConfig object. This will perform partial validation, by e.g. checking that the maximum dimensions are at least as large as the optimum dimensions, and that the optimum dimensions are always as least as large as the minimum dimensions. Some validation steps require knowledge of the network definition and are deferred to engine build time. + + :ivar extra_memory_target: Additional memory that the builder should aim to maximally allocate for this profile, as a fraction of the memory it would use if the user did not impose any constraints on memory. This unconstrained case is the default; it corresponds to ``extra_memory_target`` == 1.0. If ``extra_memory_target`` == 0.0, the builder aims to create the new optimization profile without allocating any additional weight memory. Valid inputs lie between 0.0 and 1.0. This parameter is only a hint, and TensorRT does not guarantee that the ``extra_memory_target`` will be reached. This parameter is ignored for the first (default) optimization profile that is defined. +)trtdoc"; + +constexpr const char* set_shape = R"trtdoc( + Set the minimum/optimum/maximum dimensions for a dynamic input tensor. + + This function must be called for any network input tensor that has dynamic dimensions. If ``min``, ``opt``, and ``max`` are the minimum, optimum, and maximum dimensions, and ``real_shape`` is the shape for this input tensor provided to the :class:`INetworkDefinition` ,then the following conditions must hold: + + (1) ``len(min)`` == ``len(opt)`` == ``len(max)`` == ``len(real_shape)`` + (2) 1 <= ``min[i]`` <= ``opt[i]`` <= ``max[i]`` for all ``i`` + (3) if ``real_shape[i]`` != -1, then ``min[i]`` == ``opt[i]`` == ``max[i]`` == ``real_shape[i]`` + + This function may (but need not be) called for an input tensor that does not have dynamic dimensions. In this + case, all shapes must equal ``real_shape``. + + :arg input: The name of the input tensor. + :arg min: The minimum dimensions for this input tensor. + :arg opt: The optimum dimensions for this input tensor. + :arg max: The maximum dimensions for this input tensor. + + :raises: :class:`ValueError` if an inconsistency was detected. Note that inputs can be validated only partially; a full validation is performed at engine build time. +)trtdoc"; + +constexpr const char* get_shape = R"trtdoc( + Get the minimum/optimum/maximum dimensions for a dynamic input tensor. + If the dimensions have not been previously set via :func:`set_shape`, return an invalid :class:`Dims` with a length of -1. + + :returns: A ``List[Dims]`` of length 3, containing the minimum, optimum, and maximum shapes, in that order. If the shapes have not been set yet, an empty list is returned. +)trtdoc"; + +constexpr const char* set_shape_input = R"trtdoc( + Set the minimum/optimum/maximum values for a shape input tensor. + + This function must be called for every input tensor ``t`` that is a shape tensor (``t.is_shape`` == ``True``). + This implies that the datatype of ``t`` is ``int32``, the rank is either 0 or 1, and the dimensions of ``t`` + are fixed at network definition time. This function must NOT be called for any input tensor that is not a + shape tensor. + + If ``min``, ``opt``, and ``max`` are the minimum, optimum, and maximum values, it must be true that ``min[i]`` <= ``opt[i]`` <= ``max[i]`` for + all ``i``. + + :arg input: The name of the input tensor. + :arg min: The minimum values for this shape tensor. + :arg opt: The optimum values for this shape tensor. + :arg max: The maximum values for this shape tensor. + + :raises: :class:`ValueError` if an inconsistency was detected. Note that inputs can be validated only partially; a full validation is performed at engine build time. +)trtdoc"; + +constexpr const char* get_shape_input = R"trtdoc( + Get the minimum/optimum/maximum values for a shape input tensor. + + :returns: A ``List[List[int]]`` of length 3, containing the minimum, optimum, and maximum values, in that order. If the values have not been set yet, an empty list is returned. +)trtdoc"; +} // namespace IOptimizationProfileDoc + +namespace ErrorCodeDoc +{ +constexpr const char* descr = R"trtdoc(Error codes that can be returned by TensorRT during execution.)trtdoc"; + +constexpr const char* SUCCESS = R"trtdoc(Execution completed successfully.)trtdoc"; + +constexpr const char* UNSPECIFIED_ERROR = R"trtdoc( + An error that does not fall into any other category. This error is included for forward compatibility. +)trtdoc"; + +constexpr const char* INTERNAL_ERROR = R"trtdoc(A non-recoverable TensorRT error occurred.)trtdoc"; + +constexpr const char* INVALID_ARGUMENT = R"trtdoc( + An argument passed to the function is invalid in isolation. This is a violation of the API contract. +)trtdoc"; + +constexpr const char* INVALID_CONFIG = R"trtdoc( + An error occurred when comparing the state of an argument relative to other arguments. For example, the + dimensions for concat differ between two tensors outside of the channel dimension. This error is triggered + when an argument is correct in isolation, but not relative to other arguments. This is to help to distinguish + from the simple errors from the more complex errors. + This is a violation of the API contract. +)trtdoc"; + +constexpr const char* FAILED_ALLOCATION = R"trtdoc( + An error occurred when performing an allocation of memory on the host or the device. + A memory allocation error is normally fatal, but in the case where the application provided its own memory + allocation routine, it is possible to increase the pool of available memory and resume execution. +)trtdoc"; + +constexpr const char* FAILED_INITIALIZATION = R"trtdoc( + One, or more, of the components that TensorRT relies on did not initialize correctly. + This is a system setup issue. +)trtdoc"; + +constexpr const char* FAILED_EXECUTION = R"trtdoc( + An error occurred during execution that caused TensorRT to end prematurely, either an asynchronous error or + other execution errors reported by CUDA/DLA. In a dynamic system, the + data can be thrown away and the next frame can be processed or execution can be retried. + This is either an execution error or a memory error. +)trtdoc"; + +constexpr const char* FAILED_COMPUTATION = R"trtdoc( + An error occurred during execution that caused the data to become corrupted, but execution finished. Examples + of this error are NaN squashing or integer overflow. In a dynamic system, the data can be thrown away and the + next frame can be processed or execution can be retried. + This is either a data corruption error, an input error, or a range error. +)trtdoc"; + +constexpr const char* INVALID_STATE = R"trtdoc( + TensorRT was put into a bad state by incorrect sequence of function calls. An example of an invalid state is + specifying a layer to be DLA only without GPU fallback, and that layer is not supported by DLA. This can occur + in situations where a service is optimistically executing networks for multiple different configurations + without checking proper error configurations, and instead throwing away bad configurations caught by TensorRT. + This is a violation of the API contract, but can be recoverable. + + Example of a recovery: + GPU fallback is disabled and conv layer with large filter(63x63) is specified to run on DLA. This will fail due + to DLA not supporting the large kernel size. This can be recovered by either turning on GPU fallback + or setting the layer to run on the GPU. +)trtdoc"; + +constexpr const char* UNSUPPORTED_STATE = R"trtdoc( + An error occurred due to the network not being supported on the device due to constraints of the hardware or + system. An example is running a unsafe layer in a safety certified context, or a resource requirement for the + current network is greater than the capabilities of the target device. The network is otherwise correct, but + the network and hardware combination is problematic. This can be recoverable. + Examples: + * Scratch space requests larger than available device memory and can be recovered by increasing allowed workspace size. + * Tensor size exceeds the maximum element count and can be recovered by reducing the maximum batch size. +)trtdoc"; +} // namespace ErrorCodeDoc + +namespace IErrorRecorderDoc +{ +constexpr const char* descr = R"trtdoc( + Reference counted application-implemented error reporting interface for TensorRT objects. + + The error reporting mechanism is a user defined object that interacts with the internal state of the object + that it is assigned to in order to determine information about abnormalities in execution. The error recorder + gets both an error enum that is more descriptive than pass/fail and also a description that gives more + detail on the exact failure modes. In the safety context, the error strings are all limited to 128 characters + in length. + The ErrorRecorder gets passed along to any class that is created from another class that has an ErrorRecorder + assigned to it. For example, assigning an ErrorRecorder to an Builder allows all INetwork's, ILayer's, and + ITensor's to use the same error recorder. For functions that have their own ErrorRecorder accessor functions. + This allows registering a different error recorder or de-registering of the error recorder for that specific + object. + + The ErrorRecorder object implementation must be thread safe if the same ErrorRecorder is passed to different + interface objects being executed in parallel in different threads. All locking and synchronization is + pushed to the interface implementation and TensorRT does not hold any synchronization primitives when accessing + the interface functions. +)trtdoc"; + +constexpr const char* has_overflowed = R"trtdoc( + Determine if the error stack has overflowed. + + In the case when the number of errors is large, this function is used to query if one or more + errors have been dropped due to lack of storage capacity. This is especially important in the + automotive safety case where the internal error handling mechanisms cannot allocate memory. + + :returns: True if errors have been dropped due to overflowing the error stack. +)trtdoc"; + +constexpr const char* get_num_errors = R"trtdoc( + Return the number of errors + + Determines the number of errors that occurred between the current point in execution + and the last time that the clear() was executed. Due to the possibility of asynchronous + errors occuring, a TensorRT API can return correct results, but still register errors + with the Error Recorder. The value of getNbErrors must monotonically increases until clear() + is called. + + :returns: Returns the number of errors detected, or 0 if there are no errors. +)trtdoc"; + +constexpr const char* get_error_code = R"trtdoc( + Returns the ErrorCode enumeration. + + The error_idx specifies what error code from 0 to :attr:`num_errors`-1 that the application + wants to analyze and return the error code enum. + + :arg error_idx: A 32bit integer that indexes into the error array. + + :returns: Returns the enum corresponding to error_idx. +)trtdoc"; + +constexpr const char* get_error_desc = R"trtdoc( + Returns description of the error. + + For the error specified by the idx value, return description of the error. In the safety context there is a + constant length requirement to remove any dynamic memory allocations and the error message + may be truncated. The format of the error description is " - ". + + :arg error_idx: A 32bit integer that indexes into the error array. + + :returns: Returns description of the error. +)trtdoc"; + +constexpr const char* clear = R"trtdoc( + Clear the error stack on the error recorder. + + Removes all the tracked errors by the error recorder. This function must guarantee that after + this function is called, and as long as no error occurs, :attr:`num_errors` will be zero. +)trtdoc"; + +constexpr const char* report_error = R"trtdoc( + Clear the error stack on the error recorder. + + Report an error to the user that has a given value and human readable description. The function returns false + if processing can continue, which implies that the reported error is not fatal. This does not guarantee that + processing continues, but provides a hint to TensorRT. + + :arg val: The error code enum that is being reported. + :arg desc: The description of the error. + + :returns: True if the error is determined to be fatal and processing of the current function must end. +)trtdoc"; +} // namespace IErrorRecorderDoc + +namespace IExecutionContextDoc +{ +constexpr const char* descr = R"trtdoc( + Context for executing inference using an :class:`ICudaEngine` . + Multiple :class:`IExecutionContext` s may exist for one :class:`ICudaEngine` instance, allowing the same + :class:`ICudaEngine` to be used for the execution of multiple batches simultaneously. + + :ivar debug_sync: :class:`bool` The debug sync flag. If this flag is set to true, the :class:`ICudaEngine` will log the successful execution for each kernel during execute(). It has no effect when using execute_async(). + :ivar profiler: :class:`IProfiler` The profiler in use by this :class:`IExecutionContext` . + :ivar engine: :class:`ICudaEngine` The associated :class:`ICudaEngine` . + :ivar name: :class:`str` The name of the :class:`IExecutionContext` . + :ivar device_memory: :class:`capsule` The device memory for use by this execution context. The memory must be aligned on a 256-byte boundary, and its size must be at least :attr:`engine.device_memory_size`. If using :func:`execute_async` to run the network, The memory is in use from the invocation of :func:`execute_async` until network execution is complete. If using :func:`execute`, it is in use until :func:`execute` returns. Releasing or otherwise using the memory for other purposes during this time will result in undefined behavior. + :ivar active_optimization_profile: :class:`int` The active optimization profile for the context. The selected profile will be used in subsequent calls to :func:`execute` or :func:`execute_async` . Profile 0 is selected by default. Changing this value will invalidate all dynamic bindings for the current execution context, so that they have to be set again using :func:`set_binding_shape` before calling either :func:`execute` or :func:`execute_async` . + :ivar all_binding_shapes_specified: :class:`bool` Whether all dynamic dimensions of input tensors have been specified by calling :func:`set_binding_shape` . Trivially true if network has no dynamically shaped input tensors. + :ivar all_shape_inputs_specified: :class:`bool` Whether values for all input shape tensors have been specified by calling :func:`set_shape_input` . Trivially true if network has no input shape bindings. + :ivar error_recorder: :class:`IErrorRecorder` Application-implemented error reporting interface for TensorRT objects. +)trtdoc"; - constexpr const char* set_binding_shape = R"trtdoc( - Set the dynamic shape of a binding. +constexpr const char* execute = R"trtdoc( + Synchronously execute inference on a batch. + This method requires a array of input and output buffers. The mapping from tensor names to indices can be queried using :func:`ICudaEngine.get_binding_index()` . - Requires the engine to be built without an implicit batch dimension. - The binding must be an input tensor, and all dimensions must be compatible with - the network definition (i.e. only the wildcard dimension -1 can be replaced with a - new dimension > 0). Furthermore, the dimensions must be in the valid range for the - currently selected optimization profile. + :arg batch_size: The batch size. This is at most the value supplied when the :class:`ICudaEngine` was built. + :arg bindings: A list of integers representing input and output buffer addresses for the network. - For all dynamic non-output bindings (which have at least one wildcard dimension of -1), - this method needs to be called after setting :attr:`active_optimization_profile` before - either :func:`execute_async` or :func:`execute` may be called. When all input shapes have been - specified, :attr:`all_binding_shapes_specified` is set to :class:`True` . + :returns: True if execution succeeded. +)trtdoc"; - :arg binding: The binding index. - :arg shape: The shape to set. +constexpr const char* execute_async = R"trtdoc( + Asynchronously execute inference on a batch. + This method requires a array of input and output buffers. The mapping from tensor names to indices can be queried using :func:`ICudaEngine::get_binding_index()` . - :returns: :class:`False` if an error occurs (e.g. index out of range), else :class:`True` . - )trtdoc"; + :arg batch_size: The batch size. This is at most the value supplied when the :class:`ICudaEngine` was built. + :arg bindings: A list of integers representing input and output buffer addresses for the network. + :arg stream_handle: A handle for a CUDA stream on which the inference kernels will be executed. + :arg input_consumed: An optional event which will be signaled when the input buffers can be refilled with new data - constexpr const char* get_binding_shape = R"trtdoc( - Get the dynamic shape of a binding. + :returns: True if the kernels were executed successfully. +)trtdoc"; - If :func:`set_binding_shape` has been called on this binding (or if there are no - dynamic dimensions), all dimensions will be positive. Otherwise, it is necessary to - call :func:`set_binding_shape` before :func:`execute_async` or :func:`execute` may be called. +constexpr const char* execute_v2 = R"trtdoc( + Synchronously execute inference on a batch. + This method requires a array of input and output buffers. The mapping from tensor names to indices can be queried using :func:`ICudaEngine.get_binding_index()` . + This method only works for execution contexts built from networks with no implicit batch dimension. - If the ``binding`` is out of range, an invalid Dims with nbDims == -1 is returned. + :arg bindings: A list of integers representing input and output buffer addresses for the network. - If ``ICudaEngine.binding_is_input(binding)`` is :class:`False` , then both - :attr:`all_binding_shapes_specified` and :attr:`all_shape_inputs_specified` must be :class:`True` - before calling this method. + :returns: True if execution succeeded. +)trtdoc"; - :arg binding: The binding index. +constexpr const char* execute_async_v2 = R"trtdoc( + Asynchronously execute inference on a batch. + This method requires a array of input and output buffers. The mapping from tensor names to indices can be queried using :func:`ICudaEngine::get_binding_index()` . + This method only works for execution contexts built from networks with no implicit batch dimension. - :returns: A :class:`Dims` object representing the currently selected shape. - )trtdoc"; + :arg bindings: A list of integers representing input and output buffer addresses for the network. + :arg stream_handle: A handle for a CUDA stream on which the inference kernels will be executed. + :arg input_consumed: An optional event which will be signaled when the input buffers can be refilled with new data - constexpr const char* set_shape_input = R"trtdoc( - Set values of an input shape tensor required by shape calculations. + :returns: True if the kernels were executed successfully. +)trtdoc"; - :arg binding: The binding index of an input tensor for which ``ICudaEngine.is_shape_binding(binding)`` and ``ICudaEngine.binding_is_input(binding)`` are both true. - :arg shape: An iterable containing the values of the input shape tensor. The number of values should be the product of the dimensions returned by ``get_binding_shape(binding)``. +// TODO: Check if this makes sense to have. +constexpr const char* device_memory = R"trtdoc( + The device memory for use by this :class:`IExecutionContext` . - If ``ICudaEngine.is_shape_binding(binding)`` and ``ICudaEngine.binding_is_input(binding)`` are both true, this method must be called before :func:`execute_async` or :func:`execute` may be called. Additionally, this method must not be called if either ``ICudaEngine.is_shape_binding(binding)`` or ``ICudaEngine.binding_is_input(binding)`` are false. + The memory must be aligned on a 256-byte boundary, and its size must be at least that + returned by getDeviceMemorySize(). If using :func:`execute_async()` to run the network, The memory is in + use from the invocation of :func:`execute_async()` until network execution is complete. If using :func:`execute()`, + it is in use until :func:`execute()` returns. Releasing or otherwise using the memory for other + purposes during this time will result in undefined behavior. +)trtdoc"; - :returns: :class:`True` if the values were set successfully. - )trtdoc"; +constexpr const char* get_strides = R"trtdoc( + Return the strides of the buffer for the given binding. - constexpr const char* get_shape = R"trtdoc( - Get values of an input shape tensor required for shape calculations or an output tensor produced by shape calculations. + Note that strides can be different for different execution contexts with dynamic shapes. - :arg binding: The binding index of an input tensor for which ``ICudaEngine.is_shape_binding(binding)`` is true. + :arg binding: The binding index. +)trtdoc"; - If ``ICudaEngine.binding_is_input(binding) == False``, then both - :attr:`all_binding_shapes_specified` and :attr:`all_shape_inputs_specified` must be :class:`True` - before calling this method. +constexpr const char* set_binding_shape = R"trtdoc( + Set the dynamic shape of a binding. - :returns: An iterable containing the values of the shape tensor. - )trtdoc"; + Requires the engine to be built without an implicit batch dimension. + The binding must be an input tensor, and all dimensions must be compatible with + the network definition (i.e. only the wildcard dimension -1 can be replaced with a + new dimension > 0). Furthermore, the dimensions must be in the valid range for the + currently selected optimization profile. - constexpr const char* set_optimization_profile_async = R"trtdoc( - Set the optimization profile with async semantics + For all dynamic non-output bindings (which have at least one wildcard dimension of -1), + this method needs to be called after setting :attr:`active_optimization_profile` before + either :func:`execute_async` or :func:`execute` may be called. When all input shapes have been + specified, :attr:`all_binding_shapes_specified` is set to :class:`True` . - :arg profile_index: The index of the optimization profile + :arg binding: The binding index. + :arg shape: The shape to set. - :arg stream_handle: cuda stream on which the work to switch optimization profile can be enqueued + :returns: :class:`False` if an error occurs (e.g. specified binding is out of range for the currently selected optimization profile or specified shape is inconsistent with min-max range of the optimization profile), else :class:`True`. - When an optimization profile is switched via this API, TensorRT may require that data is copied via cudaMemcpyAsync. It is the - application’s responsibility to guarantee that synchronization between the profile sync stream and the enqueue stream occurs. + Note that the network can still be invalid for + certain combinations of input shapes that lead to invalid output shapes. To confirm the correctness + of the network input shapes, check whether the output binding has valid + shape using :func:`get_binding_shape` on the output binding. +)trtdoc"; - :returns: :class:`True` if the optimization profile was set successfully - )trtdoc"; - } //IExecutionContextDoc +constexpr const char* get_binding_shape = R"trtdoc( + Get the dynamic shape of a binding. - namespace ICudaEngineDoc - { - constexpr const char* descr = R"trtdoc( - An :class:`ICudaEngine` for executing inference on a built network. + If :func:`set_binding_shape` has been called on this binding (or if there are no + dynamic dimensions), all dimensions will be positive. Otherwise, it is necessary to + call :func:`set_binding_shape` before :func:`execute_async` or :func:`execute` may be called. - The engine can be indexed with ``[]`` . When indexed in this way with an integer, it will return the corresponding binding name. When indexed with a string, it will return the corresponding binding index. + If the ``binding`` is out of range, an invalid Dims with nbDims == -1 is returned. - :ivar num_bindings: :class:`int` The number of binding indices. - :ivar max_batch_size: :class:`int` The maximum batch size which can be used for inference. For an engine built from an :class:`INetworkDefinition` without an implicit batch dimension, this will always be ``1`` . - :ivar has_implicit_batch_dimension: :class:`bool` Whether the engine was built with an implicit batch dimension.. This is an engine-wide property. Either all tensors in the engine have an implicit batch dimension or none of them do. This is True if and only if the :class:`INetworkDefinition` from which this engine was built was created with the ``NetworkDefinitionCreationFlag.EXPLICIT_BATCH`` flag. - :ivar num_layers: :class:`int` The number of layers in the network. The number of layers in the network is not necessarily the number in the original :class:`INetworkDefinition`, as layers may be combined or eliminated as the :class:`ICudaEngine` is optimized. This value can be useful when building per-layer tables, such as when aggregating profiling data over a number of executions. - :ivar max_workspace_size: :class:`int` The amount of workspace the :class:`ICudaEngine` uses. The workspace size will be no greater than the value provided to the :class:`Builder` when the :class:`ICudaEngine` was built, and will typically be smaller. Workspace will be allocated for each :class:`IExecutionContext` . - :ivar device_memory_size: :class:`int` The amount of device memory required by an :class:`IExecutionContext` . - :ivar refittable: :class:`bool` Whether the engine can be refit. - :ivar name: :class:`str` The name of the network associated with the engine. The name is set during network creation and is retrieved after building or deserialization. - :ivar num_optimization_profiles: :class:`int` The number of optimization profiles defined for this engine. This is always at least 1. - )trtdoc"; + If ``ICudaEngine.binding_is_input(binding)`` is :class:`False` , then both + :attr:`all_binding_shapes_specified` and :attr:`all_shape_inputs_specified` must be :class:`True` + before calling this method. - constexpr const char* get_binding_index = R"trtdoc( - Retrieve the binding index for a named tensor. + :arg binding: The binding index. - You can also use engine's :func:`__getitem__` with ``engine[name]``. When invoked with a :class:`str` , this will return the corresponding binding index. + :returns: A :class:`Dims` object representing the currently selected shape. +)trtdoc"; - :func:`IExecutionContext.execute_async()` and :func:`IExecutionContext.execute()` require an array of buffers. - Engine bindings map from tensor names to indices in this array. - Binding indices are assigned at :class:`ICudaEngine` build time, and take values in the range [0 ... n-1] where n is the total number of inputs and outputs. +constexpr const char* set_shape_input = R"trtdoc( + Set values of an input shape tensor required by shape calculations. - :arg name: The tensor name. + :arg binding: The binding index of an input tensor for which ``ICudaEngine.is_shape_binding(binding)`` and ``ICudaEngine.binding_is_input(binding)`` are both true. + :arg shape: An iterable containing the values of the input shape tensor. The number of values should be the product of the dimensions returned by ``get_binding_shape(binding)``. - :returns: The binding index for the named tensor, or -1 if the name is not found. - )trtdoc"; + If ``ICudaEngine.is_shape_binding(binding)`` and ``ICudaEngine.binding_is_input(binding)`` are both true, this method must be called before :func:`execute_async` or :func:`execute` may be called. Additionally, this method must not be called if either ``ICudaEngine.is_shape_binding(binding)`` or ``ICudaEngine.binding_is_input(binding)`` are false. - constexpr const char* get_binding_name = R"trtdoc( - Retrieve the name corresponding to a binding index. + :returns: :class:`False` if an error occurs (e.g. specified binding is out of range for the currently selected optimization profile or specified shape values are inconsistent with min-max range of the optimization profile), else :class:`True`. - You can also use engine's :func:`__getitem__` with ``engine[index]``. When invoked with an :class:`int` , this will return the corresponding binding name. + Note that the network can still be invalid for + certain combinations of input shapes that lead to invalid output shapes. To confirm the correctness + of the network input shapes, check whether the output binding has valid + shape using :func:`get_binding_shape` on the output binding. +)trtdoc"; - This is the reverse mapping to that provided by :func:`get_binding_index()` . +constexpr const char* get_shape = R"trtdoc( + Get values of an input shape tensor required for shape calculations or an output tensor produced by shape calculations. - :arg index: The binding index. + :arg binding: The binding index of an input tensor for which ``ICudaEngine.is_shape_binding(binding)`` is true. - :returns: The name corresponding to the binding index. - )trtdoc"; + If ``ICudaEngine.binding_is_input(binding) == False``, then both + :attr:`all_binding_shapes_specified` and :attr:`all_shape_inputs_specified` must be :class:`True` + before calling this method. - // Documentation bug with parameters on these three functions because they are overloaded. - constexpr const char* binding_is_input = R"trtdoc( - Determine whether a binding is an input binding. + :returns: An iterable containing the values of the shape tensor. +)trtdoc"; - :index: The binding index. +constexpr const char* set_optimization_profile_async = R"trtdoc( + Set the optimization profile with async semantics - :returns: True if the index corresponds to an input binding and the index is in range. - )trtdoc"; + :arg profile_index: The index of the optimization profile - constexpr const char* binding_is_input_str = R"trtdoc( - Determine whether a binding is an input binding. + :arg stream_handle: cuda stream on which the work to switch optimization profile can be enqueued - :name: The name of the tensor corresponding to an engine binding. + When an optimization profile is switched via this API, TensorRT may require that data is copied via cudaMemcpyAsync. It is the + application’s responsibility to guarantee that synchronization between the profile sync stream and the enqueue stream occurs. - :returns: True if the index corresponds to an input binding and the index is in range. - )trtdoc"; + :returns: :class:`True` if the optimization profile was set successfully +)trtdoc"; +} // namespace IExecutionContextDoc - constexpr const char* get_binding_shape = R"trtdoc( - Get the shape of a binding. +namespace ICudaEngineDoc +{ +constexpr const char* descr = R"trtdoc( + An :class:`ICudaEngine` for executing inference on a built network. - :index: The binding index. + The engine can be indexed with ``[]`` . When indexed in this way with an integer, it will return the corresponding binding name. When indexed with a string, it will return the corresponding binding index. - :Returns: The shape of the binding if the index is in range, otherwise Dims() - )trtdoc"; + :ivar num_bindings: :class:`int` The number of binding indices. + :ivar max_batch_size: :class:`int` The maximum batch size which can be used for inference. For an engine built from an :class:`INetworkDefinition` without an implicit batch dimension, this will always be ``1`` . + :ivar has_implicit_batch_dimension: :class:`bool` Whether the engine was built with an implicit batch dimension.. This is an engine-wide property. Either all tensors in the engine have an implicit batch dimension or none of them do. This is True if and only if the :class:`INetworkDefinition` from which this engine was built was created with the ``NetworkDefinitionCreationFlag.EXPLICIT_BATCH`` flag. + :ivar num_layers: :class:`int` The number of layers in the network. The number of layers in the network is not necessarily the number in the original :class:`INetworkDefinition`, as layers may be combined or eliminated as the :class:`ICudaEngine` is optimized. This value can be useful when building per-layer tables, such as when aggregating profiling data over a number of executions. + :ivar max_workspace_size: :class:`int` The amount of workspace the :class:`ICudaEngine` uses. The workspace size will be no greater than the value provided to the :class:`Builder` when the :class:`ICudaEngine` was built, and will typically be smaller. Workspace will be allocated for each :class:`IExecutionContext` . + :ivar device_memory_size: :class:`int` The amount of device memory required by an :class:`IExecutionContext` . + :ivar refittable: :class:`bool` Whether the engine can be refit. + :ivar name: :class:`str` The name of the network associated with the engine. The name is set during network creation and is retrieved after building or deserialization. + :ivar num_optimization_profiles: :class:`int` The number of optimization profiles defined for this engine. This is always at least 1. + :ivar error_recorder: :class:`IErrorRecorder` Application-implemented error reporting interface for TensorRT objects. + :ivar engine_capability: :class:`EngineCapability` The engine capability. See :class:`EngineCapability` for details. + :ivar tactic_sources: :class:`int` The tactic sources required by this engine +)trtdoc"; - constexpr const char* get_binding_shape_str = R"trtdoc( - Get the shape of a binding. +constexpr const char* get_binding_index = R"trtdoc( + Retrieve the binding index for a named tensor. - :name: The name of the tensor corresponding to an engine binding. + You can also use engine's :func:`__getitem__` with ``engine[name]``. When invoked with a :class:`str` , this will return the corresponding binding index. - :Returns: The shape of the binding if the tensor is present, otherwise Dims() - )trtdoc"; + :func:`IExecutionContext.execute_async()` and :func:`IExecutionContext.execute()` require an array of buffers. + Engine bindings map from tensor names to indices in this array. + Binding indices are assigned at :class:`ICudaEngine` build time, and take values in the range [0 ... n-1] where n is the total number of inputs and outputs. - constexpr const char* get_binding_dtype = R"trtdoc( - Determine the required data type for a buffer from its binding index. + :arg name: The tensor name. - :index: The binding index. + :returns: The binding index for the named tensor, or -1 if the name is not found. +)trtdoc"; - :Returns: The type of data in the buffer. - )trtdoc"; +constexpr const char* get_binding_name = R"trtdoc( + Retrieve the name corresponding to a binding index. - constexpr const char* get_binding_dtype_str = R"trtdoc( - Determine the required data type for a buffer from its binding index. + You can also use engine's :func:`__getitem__` with ``engine[index]``. When invoked with an :class:`int` , this will return the corresponding binding name. - :name: The name of the tensor corresponding to an engine binding. + This is the reverse mapping to that provided by :func:`get_binding_index()` . - :Returns: The type of data in the buffer. - )trtdoc"; + :arg index: The binding index. - constexpr const char* serialize = R"trtdoc( - Serialize the engine to a stream. + :returns: The name corresponding to the binding index. +)trtdoc"; - :returns: An :class:`IHostMemory` object containing the serialized :class:`ICudaEngine` . - )trtdoc"; +// Documentation bug with parameters on these three functions because they are overloaded. +constexpr const char* binding_is_input = R"trtdoc( + Determine whether a binding is an input binding. - constexpr const char* create_execution_context = R"trtdoc( - Create an :class:`IExecutionContext` . + :index: The binding index. - :returns: The newly created :class:`IExecutionContext` . - )trtdoc"; + :returns: True if the index corresponds to an input binding and the index is in range. +)trtdoc"; - constexpr const char* get_location = R"trtdoc( - Get location of binding. - This lets you know whether the binding should be a pointer to device or host memory. +constexpr const char* binding_is_input_str = R"trtdoc( + Determine whether a binding is an input binding. - :index: The binding index. + :name: The name of the tensor corresponding to an engine binding. - :returns: The location of the bound tensor with given index. - )trtdoc"; + :returns: True if the index corresponds to an input binding and the index is in range. +)trtdoc"; - constexpr const char* get_location_str = R"trtdoc( - Get location of binding. - This lets you know whether the binding should be a pointer to device or host memory. +constexpr const char* get_binding_shape = R"trtdoc( + Get the shape of a binding. - :name: The name of the tensor corresponding to an engine binding. + :index: The binding index. - :returns: The location of the bound tensor with given index. - )trtdoc"; + :Returns: The shape of the binding if the index is in range, otherwise Dims() +)trtdoc"; - constexpr const char* create_execution_context_without_device_memory = R"trtdoc( - Create an :class:`IExecutionContext` without any device memory allocated - The memory for execution of this device context must be supplied by the application. +constexpr const char* get_binding_shape_str = R"trtdoc( + Get the shape of a binding. - :returns: An :class:`IExecutionContext` without device memory allocated. - )trtdoc"; + :name: The name of the tensor corresponding to an engine binding. - constexpr const char* get_profile_shape = R"trtdoc( - Get the minimum/optimum/maximum dimensions for a particular binding under an optimization profile. + :Returns: The shape of the binding if the tensor is present, otherwise Dims() +)trtdoc"; - :arg profile_index: The index of the profile. - :arg binding: The binding index or name. +constexpr const char* get_binding_dtype = R"trtdoc( + Determine the required data type for a buffer from its binding index. - :returns: A ``List[Dims]`` of length 3, containing the minimum, optimum, and maximum shapes, in that order. - )trtdoc"; + :index: The binding index. - constexpr const char* get_profile_shape_input = R"trtdoc( - Get minimum/optimum/maximum values for an input shape binding under an optimization profile. If the specified binding is not an input shape binding, an exception is raised. + :Returns: The type of data in the buffer. +)trtdoc"; - :arg profile_index: The index of the profile. - :arg binding: The binding index or name. +constexpr const char* get_binding_dtype_str = R"trtdoc( + Determine the required data type for a buffer from its binding index. - :returns: A ``List[List[int]]`` of length 3, containing the minimum, optimum, and maximum values, in that order. If the values have not been set yet, an empty list is returned. - )trtdoc"; + :name: The name of the tensor corresponding to an engine binding. - constexpr const char* is_shape_binding = R"trtdoc( - Returns :class:`True` if tensor is required as input for shape calculations or output from them. + :Returns: The type of data in the buffer. +)trtdoc"; - TensorRT evaluates a network in two phases: +constexpr const char* serialize = R"trtdoc( + Serialize the engine to a stream. - 1. Compute shape information required to determine memory allocation requirements and validate that runtime sizes make sense. + :returns: An :class:`IHostMemory` object containing the serialized :class:`ICudaEngine` . +)trtdoc"; - 2. Process tensors on the device. +constexpr const char* create_execution_context = R"trtdoc( + Create an :class:`IExecutionContext` . - Some tensors are required in phase 1. These tensors are called "shape tensors", and always - have type :class:`tensorrt.int32` and no more than one dimension. These tensors are not always shapes - themselves, but might be used to calculate tensor shapes for phase 2. + :returns: The newly created :class:`IExecutionContext` . +)trtdoc"; - :func:`is_shape_binding` returns true if the tensor is a required input or an output computed in phase 1. - :func:`is_execution_binding` returns true if the tensor is a required input or an output computed in phase 2. +constexpr const char* get_location = R"trtdoc( + Get location of binding. + This lets you know whether the binding should be a pointer to device or host memory. - For example, if a network uses an input tensor with binding ``i`` as an input to an IElementWiseLayer that computes the reshape dimensions for an :class:`IShuffleLayer` , ``is_shape_binding(i) == True`` + :index: The binding index. - It's possible to have a tensor be required by both phases. For instance, a tensor can be used as a shape in an :class:`IShuffleLayer` and as the indices for an :class:`IGatherLayer` collecting floating-point data. + :returns: The location of the bound tensor with given index. +)trtdoc"; - It's also possible to have a tensor required by neither phase that shows up in the engine's inputs. For example, if an input tensor is used only as an input to an :class:`IShapeLayer` , only its shape matters and its values are irrelevant. +constexpr const char* get_location_str = R"trtdoc( + Get location of binding. + This lets you know whether the binding should be a pointer to device or host memory. - :arg binding: The binding index. - )trtdoc"; + :name: The name of the tensor corresponding to an engine binding. - constexpr const char* is_execution_binding = R"trtdoc( - Returns :class:`True` if tensor is required for execution phase, false otherwise. + :returns: The location of the bound tensor with given index. +)trtdoc"; - For example, if a network uses an input tensor with binding i ONLY as the reshape dimensions for an :class:`IShuffleLayer` , then ``is_execution_binding(i) == False``, and a binding of `0` can be supplied for it when calling :func:`IExecutionContext.execute` or :func:`IExecutionContext.execute_async` . +constexpr const char* create_execution_context_without_device_memory = R"trtdoc( + Create an :class:`IExecutionContext` without any device memory allocated + The memory for execution of this device context must be supplied by the application. - :arg binding: The binding index. - )trtdoc"; + :returns: An :class:`IExecutionContext` without device memory allocated. +)trtdoc"; - constexpr const char* get_binding_bytes_per_component = R"trtdoc( - Return the number of bytes per component of an element. - The vector component size is returned if :func:`get_binding_vectorized_dim` != -1. +constexpr const char* get_profile_shape = R"trtdoc( + Get the minimum/optimum/maximum dimensions for a particular binding under an optimization profile. - :arg index: The binding index. - )trtdoc"; + :arg profile_index: The index of the profile. + :arg binding: The binding index or name. - constexpr const char* get_binding_components_per_element = R"trtdoc( - Return the number of components included in one element. + :returns: A ``List[Dims]`` of length 3, containing the minimum, optimum, and maximum shapes, in that order. +)trtdoc"; - The number of elements in the vectors is returned if :func:`get_binding_vectorized_dim` != -1. +constexpr const char* get_profile_shape_input = R"trtdoc( + Get minimum/optimum/maximum values for an input shape binding under an optimization profile. If the specified binding is not an input shape binding, an exception is raised. - :arg index: The binding index. - )trtdoc"; + :arg profile_index: The index of the profile. + :arg binding: The binding index or name. - constexpr const char* get_binding_format = R"trtdoc( - Return the binding format. + :returns: A ``List[List[int]]`` of length 3, containing the minimum, optimum, and maximum values, in that order. If the values have not been set yet, an empty list is returned. +)trtdoc"; - :arg index: The binding index. - )trtdoc"; +constexpr const char* is_shape_binding = R"trtdoc( + Returns :class:`True` if tensor is required as input for shape calculations or output from them. - constexpr const char* get_binding_format_desc = R"trtdoc( - Return the human readable description of the tensor format. + TensorRT evaluates a network in two phases: - The description includes the order, vectorization, data type, strides, etc. For example: + 1. Compute shape information required to determine memory allocation requirements and validate that runtime sizes make sense. - | Example 1: kCHW + FP32 - | "Row major linear FP32 format" - | Example 2: kCHW2 + FP16 - | "Two wide channel vectorized row major FP16 format" - | Example 3: kHWC8 + FP16 + Line Stride = 32 - | "Channel major FP16 format where C % 8 == 0 and H Stride % 32 == 0" + 2. Process tensors on the device. - :arg index: The binding index. - )trtdoc"; + Some tensors are required in phase 1. These tensors are called "shape tensors", and always + have type :class:`tensorrt.int32` and no more than one dimension. These tensors are not always shapes + themselves, but might be used to calculate tensor shapes for phase 2. - constexpr const char* get_binding_vectorized_dim = R"trtdoc( - Return the dimension index that the buffer is vectorized. + :func:`is_shape_binding` returns true if the tensor is a required input or an output computed in phase 1. + :func:`is_execution_binding` returns true if the tensor is a required input or an output computed in phase 2. - Specifically -1 is returned if scalars per vector is 1. + For example, if a network uses an input tensor with binding ``i`` as an input to an IElementWiseLayer that computes the reshape dimensions for an :class:`IShuffleLayer` , ``is_shape_binding(i) == True`` - :arg index: The binding index. - )trtdoc"; + It's possible to have a tensor be required by both phases. For instance, a tensor can be used as a shape in an :class:`IShuffleLayer` and as the indices for an :class:`IGatherLayer` collecting floating-point data. - } // ICudaEngineDoc + It's also possible to have a tensor required by neither phase that shows up in the engine's inputs. For example, if an input tensor is used only as an input to an :class:`IShapeLayer` , only its shape matters and its values are irrelevant. - namespace BuilderFlagDoc - { - constexpr const char* descr - = R"trtdoc(Valid modes that the builder can enable when creating an engine from a network definition.)trtdoc"; + :arg binding: The binding index. +)trtdoc"; - constexpr const char* FP16 = R"trtdoc(Enable FP16 layer selection)trtdoc"; - constexpr const char* INT8 = R"trtdoc(Enable Int8 layer selection)trtdoc"; - constexpr const char* DEBUG = R"trtdoc(Enable debugging of layers via synchronizing after every layer)trtdoc"; - constexpr const char* GPU_FALLBACK - = R"trtdoc(Enable layers marked to execute on GPU if layer cannot execute on DLA)trtdoc"; - constexpr const char* STRICT_TYPES = R"trtdoc(Enables strict type constraints)trtdoc"; - constexpr const char* REFIT = R"trtdoc(Enable building a refittable engine)trtdoc"; - constexpr const char* DISABLE_TIMING_CACHE - = R"trtdoc(Disable reuse of timing information across identical layers.)trtdoc"; - constexpr const char* TF32 - = R"trtdoc(Allow (but not require) computations on tensors of type DataType.FLOAT to use TF32. TF32 computes inner products by rounding the inputs to 10-bit mantissas before multiplying, but accumulates the sum using 23-bit mantissas. Enabled by default.)trtdoc"; - } // namespace BuilderFlagDoc +constexpr const char* is_execution_binding = R"trtdoc( + Returns :class:`True` if tensor is required for execution phase, false otherwise. - namespace QuantizationFlagDoc - { - constexpr const char* descr = R"trtdoc(List of valid flags for quantizing the network to int8.)trtdoc"; + For example, if a network uses an input tensor with binding i ONLY as the reshape dimensions for an :class:`IShuffleLayer` , then ``is_execution_binding(i) == False``, and a binding of `0` can be supplied for it when calling :func:`IExecutionContext.execute` or :func:`IExecutionContext.execute_async` . - constexpr const char* CALIBRATE_BEFORE_FUSION - = R"trtdoc(Run int8 calibration pass before layer fusion. Only valid for IInt8LegacyCalibrator and IInt8EntropyCalibrator. We always run int8 calibration pass before layer fusion for IInt8MinMaxCalibrator and IInt8EntropyCalibrator2. Disabled by default.)trtdoc"; - } // namespace QuantizationFlagDoc + :arg binding: The binding index. +)trtdoc"; - namespace NetworkDefinitionCreationFlagDoc - { - constexpr const char* descr - = R"trtdoc(List of immutable network properties expressed at network creation time. For example, to enable explicit batch mode, pass a value of ``1 << NetworkDefinitionCreationFlag.EXPLICIT_BATCH`` to :func:`create_network` )trtdoc"; - constexpr const char* EXPLICIT_BATCH = R"trtdoc(Specify that the network should be created with an explicit batch dimension.)trtdoc"; - constexpr const char* EXPLICIT_PRECISION - = R"trtdoc(Specify that the network contains explicit quantization and dequantization scale layers.)trtdoc"; - } // NetworkDefinitionCreationFlagDoc +constexpr const char* get_binding_bytes_per_component = R"trtdoc( + Return the number of bytes per component of an element. + The vector component size is returned if :func:`get_binding_vectorized_dim` != -1. - namespace DeviceTypeDoc - { - constexpr const char* descr = R"trtdoc(Device types that TensorRT can execute on)trtdoc"; + :arg index: The binding index. +)trtdoc"; - constexpr const char* GPU = R"trtdoc(GPU device)trtdoc"; - constexpr const char* DLA = R"trtdoc(DLA core)trtdoc"; - } // DeviceTypeDoc +constexpr const char* get_binding_components_per_element = R"trtdoc( + Return the number of components included in one element. - namespace ProfilingVerbosityDoc - { - constexpr const char* descr = R"trtdoc(Profiling verbosity in NVTX annotations)trtdoc"; + The number of elements in the vectors is returned if :func:`get_binding_vectorized_dim` != -1. - constexpr const char* DEFAULT = R"trtdoc(Register layer names in NVTX message field)trtdoc"; - constexpr const char* NONE = R"trtdoc(Turn off NVTX traces)trtdoc"; - constexpr const char* VERBOSE = R"trtdoc(Register layer names in NVTX message field and register layer detail in NVTX JSON payload field)trtdoc"; - } // DeviceTypeDoc + :arg index: The binding index. +)trtdoc"; - namespace TacticSourceDoc - { - constexpr const char* descr - = R"trtdoc(List of tactic sources that can provide tactics for TensorRT.)trtdoc"; +constexpr const char* get_binding_format = R"trtdoc( + Return the binding format. - constexpr const char* CUBLAS = R"trtdoc( - Enables cuBLAS tactics. - **NOTE:** Disabling this value will cause the cublas handle passed to plugins in attachToContext to be null. - )trtdoc"; - constexpr const char* CUBLAS_LT = R"trtdoc( - Enables cuBLAS LT tactics - )trtdoc"; - } // namespace TacticSourceDoc + :arg index: The binding index. +)trtdoc"; - namespace IBuilderConfigDoc - { - constexpr const char* descr = R"trtdoc( +constexpr const char* get_binding_format_desc = R"trtdoc( + Return the human readable description of the tensor format. - :ivar min_timing_iterations: :class:`int` The number of minimization iterations used when timing layers. When timing layers, the builder minimizes over a set of average times for layer execution. This parameter controls the number of iterations used in minimization. - :ivar avg_timing_iterations: :class:`int` The number of averaging iterations used when timing layers. When timing layers, the builder minimizes over a set of average times for layer execution. This parameter controls the number of iterations used in averaging. - :ivar int8_calibrator: :class:`IInt8Calibrator` Int8 Calibration interface. The calibrator is to minimize the information loss during the INT8 quantization process. - :ivar max_workspace_size: :class:`int` The maximum workspace size. The maximum GPU temporary memory which the engine can use at execution time. - :ivar flags: :class:`int` The build mode flags to turn on builder options for this network. The flags are listed in the BuilderFlags enum. The flags set configuration options to build the network. This should be in integer consisting of one or more :class:`BuilderFlag` s, combined via binary OR. For example, ``1 << BuilderFlag.FP16 | 1 << BuilderFlag.DEBUG``. - :ivar profile_stream: :class:`int` The handle for the CUDA stream that is used to profile this network. - :ivar num_optimization_profiles: :class:`int` The number of optimization profiles. - :ivar default_device_type: :class:`tensorrt.DeviceType` The default DeviceType to be used by the Builder. - :ivar DLA_core: :class:`int` The DLA core that the engine executes on. Must be between 0 and N-1 where N is the number of available DLA cores. - :ivar profiling_verbosity: Profiling verbosity in NVTX annotations. - :ivar algorithm_selector: :class:`IAlgorithmSelector` The algorithm slector to be set/get in the build config. - )trtdoc"; + The description includes the order, vectorization, data type, strides, etc. For example: - constexpr const char* clear_flag = R"trtdoc( - clears the builder mode flag from the enabled flags. + | Example 1: kCHW + FP32 + | "Row major linear FP32 format" + | Example 2: kCHW2 + FP16 + | "Two wide channel vectorized row major FP16 format" + | Example 3: kHWC8 + FP16 + Line Stride = 32 + | "Channel major FP16 format where C % 8 == 0 and H Stride % 32 == 0" - :arg flag: The flag to clear. - )trtdoc"; + :arg index: The binding index. +)trtdoc"; - constexpr const char* set_flag = R"trtdoc( - Add the input builder mode flag to the already enabled flags. +constexpr const char* get_binding_vectorized_dim = R"trtdoc( + Return the dimension index that the buffer is vectorized. - :arg flag: The flag to set. - )trtdoc"; + Specifically -1 is returned if scalars per vector is 1. - constexpr const char* get_flag = R"trtdoc( - Check if a build mode flag is set. + :arg index: The binding index. +)trtdoc"; - :arg flag: The flag to check. +} // namespace ICudaEngineDoc - :returns: A `bool` indicating whether the flag is set. - )trtdoc"; +namespace BuilderFlagDoc +{ +constexpr const char* descr + = R"trtdoc(Valid modes that the builder can enable when creating an engine from a network definition.)trtdoc"; - constexpr const char* clear_quantization_flag = R"trtdoc( - Clears the quantization flag from the enabled quantization flags. +constexpr const char* FP16 = R"trtdoc(Enable FP16 layer selection)trtdoc"; +constexpr const char* INT8 = R"trtdoc(Enable Int8 layer selection)trtdoc"; +constexpr const char* DEBUG = R"trtdoc(Enable debugging of layers via synchronizing after every layer)trtdoc"; +constexpr const char* GPU_FALLBACK + = R"trtdoc(Enable layers marked to execute on GPU if layer cannot execute on DLA)trtdoc"; +constexpr const char* STRICT_TYPES = R"trtdoc(Enables strict type constraints)trtdoc"; +constexpr const char* REFIT = R"trtdoc(Enable building a refittable engine)trtdoc"; +constexpr const char* DISABLE_TIMING_CACHE + = R"trtdoc(Disable reuse of timing information across identical layers.)trtdoc"; +constexpr const char* TF32 + = R"trtdoc(Allow (but not require) computations on tensors of type DataType.FLOAT to use TF32. TF32 computes inner products by rounding the inputs to 10-bit mantissas before multiplying, but accumulates the sum using 23-bit mantissas. Enabled by default.)trtdoc"; +constexpr const char* SPARSE_WEIGHTS + = R"trtdoc(Allow the builder to examine weights and use optimized functions when weights have suitable sparsity.)trtdoc"; +constexpr const char* SAFETY_SCOPE + = R"trtdoc(Change the allowed parameters in the EngineCapability::kSTANDARD flow to match the restrictions that EngineCapability::kSAFETY check against for DeviceType::kGPU and EngineCapability::kDLA_STANDALONE check against the DeviceType::kDLA case. This flag is forced to true if EngineCapability::kSAFETY at build time if it is unset.)trtdoc"; +} // namespace BuilderFlagDoc - :arg flag: The flag to clear. - )trtdoc"; +namespace QuantizationFlagDoc +{ +constexpr const char* descr = R"trtdoc(List of valid flags for quantizing the network to int8.)trtdoc"; - constexpr const char* set_quantization_flag = R"trtdoc( - Add the input quantization flag to the already enabled quantization flags. - - :arg flag: The flag to set. - )trtdoc"; - - constexpr const char* get_quantization_flag = R"trtdoc( - Check if a quantization flag is set. - - :arg flag: The flag to check. - - :returns: A `bool` indicating whether the flag is set. - )trtdoc"; - - constexpr const char* reset = R"trtdoc( - Resets the builder configuration to defaults. When initializing a builder config object, we can call this function. - )trtdoc"; - - constexpr const char* add_optimization_profile = R"trtdoc( - Add an optimization profile. - - This function must be called at least once if the network has dynamic or shape input tensors. - - :arg profile: The new optimization profile, which must satisfy ``bool(profile) == True`` - - :returns: The index of the optimization profile (starting from 0) if the input is valid, or -1 if the input is - not valid. - )trtdoc"; - - constexpr const char* set_calibration_profile = R"trtdoc( - Set a calibration profile. - - Calibration optimization profile must be set if int8 calibration is used to set scales for a network with runtime dimensions. - - :arg profile: The new calibration profile, which must satisfy ``bool(profile) == True`` or be nullptr. MIN and MAX values will be overwritten by kOPT. - - :returns: True if the calibration profile was set correctly. - )trtdoc"; - - constexpr const char* get_calibration_profile = R"trtdoc( - Get the current calibration profile. - - :returns: The current calibration profile or nullptr if calibrartion profile is unset. - )trtdoc"; - - constexpr const char* set_device_type = R"trtdoc( - Set the device that this layer must execute on. If DeviceType is not set or is reset, TensorRT will use the - default DeviceType set in the builder. - - The DeviceType for a layer must be compatible with the safety flow (if specified). For example a layer - cannot be marked for DLA execution while the builder is configured for kSAFE_GPU. - - - :arg layer: The layer to set the DeviceType of - :arg device_type: The DeviceType the layer must execute on - )trtdoc"; - - constexpr const char* get_device_type = R"trtdoc( - Get the device that the layer executes on. - - :arg layer: The layer to get the DeviceType for - - :returns: The DeviceType of the layer - )trtdoc"; - - constexpr const char* is_device_type_set = R"trtdoc( - Check if the DeviceType for a layer is explicitly set. - - :arg layer: The layer to check for DeviceType - - :returns: True if DeviceType is not default, False otherwise - )trtdoc"; - - constexpr const char* reset_device_type = R"trtdoc( - Reset the DeviceType for the given layer. - - :arg layer: The layer to reset the DeviceType for - )trtdoc"; - - constexpr const char* can_run_on_DLA = R"trtdoc( - Check if the layer can run on DLA. - - :arg layer: The layer to check - - :returns: A `bool` indicating whether the layer can run on DLA - )trtdoc"; - - constexpr const char* set_tactic_sources = R"trtdoc( - Set tactic sources. - - This bitset controls which tactic sources TensorRT is allowed to use for tactic - selection. By default, kCUBLAS is always enabled, and kCUBLAS_LT is enabled for x86 - platforms, as well as non-x86 platforms if CUDA >= 11.0 - - Multiple tactic sources may be combined with a bitwise OR operation. For example, - to enable cublas and cublasLt as tactic sources, use a value of: - ``1U << static_cast(TacticSource::kCUBLAS) | 1U << static_cast(TacticSource::kCUBLAS_LT)`` - - :arg tactic_sources: The tactic sources to set - - :returns: A `bool` indicating whether the tactic sources in the build configuration were updated. The tactic sources in the build configuration will not be updated if the provided value is invalid. - )trtdoc"; - - constexpr const char* get_tactic_sources = R"trtdoc( - Get the tactic sources currently set in the engine build configuration. - )trtdoc"; - - } // namespace IBuilderConfigDoc - - namespace BuilderDoc - { - constexpr const char* descr = R"trtdoc( - Builds an :class:`ICudaEngine` from a :class:`INetworkDefinition` . - - :ivar max_batch_size: :class:`int` The maximum batch size which can be used at execution time, and also the batch size for which the :class:`ICudaEngine` will be optimized. - :ivar max_workspace_size: :class:`int` The maximum GPU temporary memory which the :class:`ICudaEngine` can use at execution time. - :ivar debug_sync: :class:`bool` Whether the :class:`Builder` should use debug synchronization. If this is true, the :class:`Builder` will synchronize after timing each layer, and report the layer name. It can be useful when diagnosing issues at build time. - :ivar min_find_iterations: :class:`int` The number of minimization iterations used when timing layers. When timing layers, the :class:`Builder` minimizes over a set of average times for layer execution. This parameter controls the number of iterations used in minimization. - :ivar average_find_iterations: :class:`int` The number of averaging iterations used when timing layers. When timing layers, the :class:`Builder` minimizes over a set of average times for layer execution. This parameter controls the number of iterations used in averaging. - :ivar platform_has_tf32: :class:`bool` Whether the platform has tf32 support. - :ivar platform_has_fast_fp16: :class:`bool` Whether the platform has fast native fp16. - :ivar platform_has_fast_int8: :class:`bool` Whether the platform has fast native int8. - :ivar int8_mode: :class:`bool` Whether Int8 mode is used. - :ivar int8_calibrator: :class:`IInt8Calibrator` The Int8 Calibration interface. - :ivar fp16_mode: :class:`bool` Whether or not 16-bit kernels are permitted. During :class:`ICudaEngine` build fp16 kernels will also be tried when this mode is enabled. - :ivar strict_type_constraints: :class:`bool` When strict type constraints is set, TensorRT will choose the type constraints that conforms to type constraints. If the flag is not enabled higher precision implementation may be chosen if it results in higher performance. - :ivar refittable: :class:`bool` Whether an :class:`ICudaEngine` will be refittable. - :ivar error_recorder: :class:`IErrorRecorder` Reference counted application-implemented error reporting interface for TensorRT objects. - )trtdoc"; - - // :ivar gpu_allocator: :class:`IGpuAllocator` The GPU allocator to be used by the :class:`Builder` . All GPU - // memory acquired will use this allocator. If set to ``None``, the default allocator will be used. - - constexpr const char* init = R"trtdoc( - :arg logger: The logger to use. - )trtdoc"; - - constexpr const char* create_network = R"trtdoc( - Create a :class:`INetworkDefinition` object. - - :arg flags: :class:`NetworkDefinitionCreationFlag` s combined using bitwise OR. Default value is 0. This mimics the behavior of create_network() in TensorRT 5.1. - - :returns: An empty TensorRT :class:`INetworkDefinition` . - )trtdoc"; - - constexpr const char* build_cuda_engine = R"trtdoc( - Builds an :class:`ICudaEngine` from a :class:`INetworkDefinition` . - - :arg network: The TensorRT :class:`INetworkDefinition` . - - :returns: A new :class:`ICudaEngine` . - )trtdoc"; - - constexpr const char* create_optimization_profile = R"trtdoc( - Create a new optimization profile. - - If the network has any dynamic input tensors, the appropriate calls to :func:`IOptimizationProfile.set_shape` must be made. Likewise, if there are any shape input tensors, the appropriate calls to :func:`IOptimizationProfile.set_shape_input` are required. - - See :class:`IOptimizationProfile` - )trtdoc"; - - constexpr const char* create_builder_config = R"trtdoc( - Create a builder configuration object. - - See :class:`IBuilderConfig` +constexpr const char* CALIBRATE_BEFORE_FUSION + = R"trtdoc(Run int8 calibration pass before layer fusion. Only valid for IInt8LegacyCalibrator and IInt8EntropyCalibrator. We always run int8 calibration pass before layer fusion for IInt8MinMaxCalibrator and IInt8EntropyCalibrator2. Disabled by default.)trtdoc"; +} // namespace QuantizationFlagDoc + +namespace NetworkDefinitionCreationFlagDoc +{ +constexpr const char* descr + = R"trtdoc(List of immutable network properties expressed at network creation time. For example, to enable explicit batch mode, pass a value of ``1 << NetworkDefinitionCreationFlag.EXPLICIT_BATCH`` to :func:`create_network` )trtdoc"; +constexpr const char* EXPLICIT_BATCH + = R"trtdoc(Specify that the network should be created with an explicit batch dimension.)trtdoc"; +constexpr const char* EXPLICIT_PRECISION + = R"trtdoc(Specify that the network contains explicit quantization and dequantization scale layers.)trtdoc"; +} // namespace NetworkDefinitionCreationFlagDoc + +namespace DeviceTypeDoc +{ +constexpr const char* descr = R"trtdoc(Device types that TensorRT can execute on)trtdoc"; + +constexpr const char* GPU = R"trtdoc(GPU device)trtdoc"; +constexpr const char* DLA = R"trtdoc(DLA core)trtdoc"; +} // namespace DeviceTypeDoc + +namespace ProfilingVerbosityDoc +{ +constexpr const char* descr = R"trtdoc(Profiling verbosity in NVTX annotations)trtdoc"; + +constexpr const char* DEFAULT = R"trtdoc(Register layer names in NVTX message field)trtdoc"; +constexpr const char* NONE = R"trtdoc(Turn off NVTX traces)trtdoc"; +constexpr const char* VERBOSE + = R"trtdoc(Register layer names in NVTX message field and register layer detail in NVTX JSON payload field)trtdoc"; +} // namespace ProfilingVerbosityDoc + +namespace TacticSourceDoc +{ +constexpr const char* descr = R"trtdoc(Tactic sources that can provide tactics for TensorRT.)trtdoc"; + +constexpr const char* CUBLAS = R"trtdoc( + Enables cuBLAS tactics. + **NOTE:** Disabling this value will cause the cublas handle passed to plugins in attachToContext to be null. + )trtdoc"; +constexpr const char* CUBLAS_LT = R"trtdoc( + Enables cuBLAS LT tactics + )trtdoc"; +constexpr const char* CUDNN = R"trtdoc( + Enables cuDNN tactics + )trtdoc"; +} // namespace TacticSourceDoc + +namespace EngineCapabilityDoc +{ +constexpr const char* descr = R"trtdoc( + List of supported engine capability flows. + The EngineCapability determines the restrictions of a network during build time and what runtime + it targets. When BuilderFlag::kSAFETY_SCOPE is not set (by default), EngineCapability.STANDARD does not provide any restrictions on functionality and the resulting + serialized engine can be executed with TensorRT's standard runtime APIs in the nvinfer1 namespace. + EngineCapability.SAFETY provides a restricted subset of network operations that are safety certified and + the resulting serialized engine can be executed with TensorRT's safe runtime APIs in the `nvinfer1::safe` namespace. + EngineCapability.DLA_STANDALONE provides a restricted subset of network operations that are DLA compatible and + the resulting serialized engine can be executed using standalone DLA runtime APIs. See sampleNvmedia for an + example of integrating NvMediaDLA APIs with TensorRT APIs.)trtdoc"; + +constexpr const char* DEFAULT + = R"trtdoc(Deprecated: Unrestricted: TensorRT mode without any restrictions using TensorRT nvinfer1 APIs.)trtdoc"; + +constexpr const char* SAFE_GPU + = R"trtdoc(Deprecated: Safety-restricted: TensorRT mode for GPU devices using TensorRT safety APIs. See safety documentation for list of supported layers and formats.)trtdoc"; + +constexpr const char* SAFE_DLA + = R"trtdoc(Deprecated: DLA-restricted: TensorRT mode for DLA devices using NvMediaDLA APIs. Only FP16 and Int8 modes are supported.)trtdoc"; + +constexpr const char* STANDARD + = R"trtdoc(Standard: TensorRT flow without targeting the standard runtime. This flow supports both DeviceType::kGPU and DeviceType::kDLA.)trtdoc"; + +constexpr const char* SAFETY + = R"trtdoc(Safety: TensorRT flow with restrictions targeting the safety runtime. See safety documentation for list of supported layers and formats. This flow supports only DeviceType::kGPU.)trtdoc"; + +constexpr const char* DLA_STANDALONE + = R"trtdoc(DLA Standalone: TensorRT flow with restrictions targeting external, to TensorRT, DLA runtimes. See DLA documentation for list of supported layers and formats. This flow supports only DeviceType::kDLA.)trtdoc"; + +} // namespace EngineCapabilityDoc + +namespace ITimingCacheDoc +{ +constexpr const char* descr = R"trtdoc( + Class to handle tactic timing info collected from builder. )trtdoc"; - constexpr const char* build_engine = R"trtdoc( - Builds an engine for the given :class:`INetworkDefinition` and :class:`IBuilderConfig` . +constexpr const char* serialize = R"trtdoc( + Serialize a timing cache to a :class:`IHostMemory` object. - This enables the builder to build multiple engines based on the same network definition, but with different builder configurations. + :returns: An :class:`IHostMemory` object that contains a serialized timing cache. + )trtdoc"; - :arg network: The TensorRT :class:`INetworkDefinition` . - :arg config: The TensorRT :class:`IBuilderConfig` . +constexpr const char* combine = R"trtdoc( + Combine input timing cache into local instance. - :returns: A new :class:`ICudaEngine` . - )trtdoc"; + Append entries in input cache to local cache. Conflicting entries will be skipped. The input + cache must be generated by a TensorRT build of exact same version, otherwise combine will be + skipped and return false. ``bool(ignore_mismatch) == True`` if combining a timing cache + created from a different device. - } /* BuilderDoc */ + :arg input_cache: The input timing cache + :arg ignore_mismatch: Whether or not to allow cache verification header mismatch - namespace RuntimeDoc - { - constexpr const char* descr = R"trtdoc( - Allows a serialized :class:`ICudaEngine` to be deserialized. - )trtdoc"; + :returns: A `bool` indicating whether the combine operation is done successfully. + )trtdoc"; - // :ivar gpu_allocator: :class:`IGpuAllocator` The GPU allocator to be used by the :class:`Runtime` . All GPU memory acquired will use this allocator. If set to None, the default allocator will be used (Default: cudaMalloc/cudaFree). +constexpr const char* reset = R"trtdoc( + Empty the timing cache - constexpr const char* init = R"trtdoc( - :arg logger: The logger to use. - )trtdoc"; + :returns: A `bool` indicating whether the reset operation is done successfully. + )trtdoc"; +} // namespace ITimingCacheDoc - constexpr const char* deserialize_cuda_engine = R"trtdoc( - Deserialize an :class:`ICudaEngine` from a stream. +namespace IBuilderConfigDoc +{ +constexpr const char* descr = R"trtdoc( - :arg serialized_engine: The :class:`buffer` that holds the serialized :class:`ICudaEngine` . - :arg plugin_factory: The :class:`IPluginFactory` , if any plugins are used by the network, otherwise None. + :ivar min_timing_iterations: :class:`int` The number of minimization iterations used when timing layers. When timing layers, the builder minimizes over a set of average times for layer execution. This parameter controls the number of iterations used in minimization. + :ivar avg_timing_iterations: :class:`int` The number of averaging iterations used when timing layers. When timing layers, the builder minimizes over a set of average times for layer execution. This parameter controls the number of iterations used in averaging. + :ivar int8_calibrator: :class:`IInt8Calibrator` Int8 Calibration interface. The calibrator is to minimize the information loss during the INT8 quantization process. + :ivar max_workspace_size: :class:`int` The maximum workspace size. The maximum GPU temporary memory which the engine can use at execution time. + :ivar flags: :class:`int` The build mode flags to turn on builder options for this network. The flags are listed in the BuilderFlags enum. The flags set configuration options to build the network. This should be in integer consisting of one or more :class:`BuilderFlag` s, combined via binary OR. For example, ``1 << BuilderFlag.FP16 | 1 << BuilderFlag.DEBUG``. + :ivar profile_stream: :class:`int` The handle for the CUDA stream that is used to profile this network. + :ivar num_optimization_profiles: :class:`int` The number of optimization profiles. + :ivar default_device_type: :class:`tensorrt.DeviceType` The default DeviceType to be used by the Builder. + :ivar DLA_core: :class:`int` The DLA core that the engine executes on. Must be between 0 and N-1 where N is the number of available DLA cores. + :ivar profiling_verbosity: Profiling verbosity in NVTX annotations. + :ivar engine_capability: The desired engine capability. See :class:`EngineCapability` for details. + )trtdoc"; - :returns: The :class:`ICudaEngine`, or None if it could not be deserialized. - )trtdoc"; +constexpr const char* clear_flag = R"trtdoc( + clears the builder mode flag from the enabled flags. - } /* RuntimeDoc */ + :arg flag: The flag to clear. + )trtdoc"; - namespace RefitterDoc - { - constexpr const char* descr = R"trtdoc( - Updates weights in an :class:`ICudaEngine` . - )trtdoc"; +constexpr const char* set_flag = R"trtdoc( + Add the input builder mode flag to the already enabled flags. - constexpr const char* init = R"trtdoc( - :arg engine: The engine to refit. - :arg logger: The logger to use. - )trtdoc"; + :arg flag: The flag to set. + )trtdoc"; - constexpr const char* set_weights = R"trtdoc( - Specify new weights for a layer of given name. - Possible reasons for rejection are: +constexpr const char* get_flag = R"trtdoc( + Check if a build mode flag is set. - * There is no such layer by that name. - * The layer does not have weights with the specified role. - * The number of weights is inconsistent with the layer’s original specification. + :arg flag: The flag to check. - Modifying the weights before :func:`refit_cuda_engine` completes will result in undefined behavior. + :returns: A `bool` indicating whether the flag is set. + )trtdoc"; - :arg layer_name: The name of the layer. - :arg role: The role of the weights. See :class:`WeightsRole` for more information. - :arg weights: The weights to refit with. +constexpr const char* clear_quantization_flag = R"trtdoc( + Clears the quantization flag from the enabled quantization flags. - :returns: ``True`` on success, or ``False`` if new weights are rejected. - )trtdoc"; + :arg flag: The flag to clear. + )trtdoc"; - constexpr const char* refit_cuda_engine = R"trtdoc( - Updates associated engine. Return ``True`` if successful. +constexpr const char* set_quantization_flag = R"trtdoc( + Add the input quantization flag to the already enabled quantization flags. - Failure occurs if :func:`get_missing` != 0 before the call. - )trtdoc"; + :arg flag: The flag to set. + )trtdoc"; - constexpr const char* get_missing = R"trtdoc( - Get description of missing weights. +constexpr const char* get_quantization_flag = R"trtdoc( + Check if a quantization flag is set. - For example, if some Weights have been set, but the engine was optimized - in a way that combines weights, any unsupplied Weights in the combination - are considered missing. + :arg flag: The flag to check. - :returns: The names of layers with missing weights, and the roles of those weights. - )trtdoc"; + :returns: A `bool` indicating whether the flag is set. + )trtdoc"; - constexpr const char* get_all = R"trtdoc( - Get description of all weights that could be refit. +constexpr const char* reset = R"trtdoc( + Resets the builder configuration to defaults. When initializing a builder config object, we can call this function. + )trtdoc"; - :returns: The names of layers with refittable weights, and the roles of those weights. - )trtdoc"; +constexpr const char* add_optimization_profile = R"trtdoc( + Add an optimization profile. - constexpr const char* get_dynamic_range = R"trtdoc( - Gets the dynamic range of a tensor. If the dynamic range was never set, returns the range computed during calibration. + This function must be called at least once if the network has dynamic or shape input tensors. - :arg tensor_name: The name of the tensor whose dynamic range to retrieve. + :arg profile: The new optimization profile, which must satisfy ``bool(profile) == True`` - :returns: :class:`Tuple[float, float]` A tuple containing the [minimum, maximum] of the dynamic range. - )trtdoc"; + :returns: The index of the optimization profile (starting from 0) if the input is valid, or -1 if the input is + not valid. +)trtdoc"; - constexpr const char* set_dynamic_range = R"trtdoc( - Update dynamic range for a tensor. +constexpr const char* set_calibration_profile = R"trtdoc( + Set a calibration profile. - :arg tensor_name: The name of the tensor whose dynamic range to update. - :arg range: The new range. + Calibration optimization profile must be set if int8 calibration is used to set scales for a network with runtime dimensions. - :returns: :class:`True` if successful, :class:`False` otherwise. + :arg profile: The new calibration profile, which must satisfy ``bool(profile) == True`` or be nullptr. MIN and MAX values will be overwritten by kOPT. - Returns false if there is no Int8 engine tensor derived from a network tensor of that name. If successful, then :func:`get_missing` may report that some weights need to be supplied. - )trtdoc"; + :returns: True if the calibration profile was set correctly. +)trtdoc"; - constexpr const char* get_tensors_with_dynamic_range = R"trtdoc( - Get names of all tensors that have refittable dynamic ranges. +constexpr const char* get_calibration_profile = R"trtdoc( + Get the current calibration profile. - :returns: The names of tensors with refittable dynamic ranges. - )trtdoc"; - } /* RefitterDoc */ + :returns: The current calibration profile or nullptr if calibrartion profile is unset. +)trtdoc"; -} /* tensorrt */ +constexpr const char* set_device_type = R"trtdoc( + Set the device that this layer must execute on. If DeviceType is not set or is reset, TensorRT will use the + default DeviceType set in the builder. + + The DeviceType for a layer must be compatible with the safety flow (if specified). For example a layer + cannot be marked for DLA execution while the builder is configured for kSAFETY. + + + :arg layer: The layer to set the DeviceType of + :arg device_type: The DeviceType the layer must execute on +)trtdoc"; + +constexpr const char* get_device_type = R"trtdoc( + Get the device that the layer executes on. + + :arg layer: The layer to get the DeviceType for + + :returns: The DeviceType of the layer +)trtdoc"; + +constexpr const char* is_device_type_set = R"trtdoc( + Check if the DeviceType for a layer is explicitly set. + + :arg layer: The layer to check for DeviceType + + :returns: True if DeviceType is not default, False otherwise +)trtdoc"; + +constexpr const char* reset_device_type = R"trtdoc( + Reset the DeviceType for the given layer. + + :arg layer: The layer to reset the DeviceType for +)trtdoc"; + +constexpr const char* can_run_on_DLA = R"trtdoc( + Check if the layer can run on DLA. + + :arg layer: The layer to check + + :returns: A `bool` indicating whether the layer can run on DLA +)trtdoc"; + +constexpr const char* set_tactic_sources = R"trtdoc( + Set tactic sources. + + This bitset controls which tactic sources TensorRT is allowed to use for tactic + selection. By default, kCUBLAS and kCUDNN are always enabled, and kCUBLAS_LT is enabled for x86 + platforms as well as non-x86 platforms when CUDA >= 11.0 + + Multiple tactic sources may be combined with a bitwise OR operation. For example, + to enable cublas and cublasLt as tactic sources, use a value of: + ``1 << int(trt.TacticSource.CUBLAS) | 1 << int(trt.TacticSource.CUBLAS_LT)`` + + :arg tactic_sources: The tactic sources to set + + :returns: A `bool` indicating whether the tactic sources in the build configuration were updated. The tactic sources in the build configuration will not be updated if the provided value is invalid. +)trtdoc"; + +constexpr const char* get_tactic_sources = R"trtdoc( + Get the tactic sources currently set in the engine build configuration. +)trtdoc"; + +constexpr const char* create_timing_cache = R"trtdoc( + Create timing cache + + Create :class:`ITimingCache` instance from serialized raw data. The created timing cache doesn't belong to + a specific builder config. It can be shared by multiple builder instances + + :arg serialized_timing_cache: The serialized timing cache. If an empty cache is provided (i.e. ``b""``), a new cache will be created. + + :returns: The created :class:`ITimingCache` object. +)trtdoc"; + +constexpr const char* set_timing_cache = R"trtdoc( + Attach a timing cache to IBuilderConfig + + The timing cache has verification header to make sure the provided cache can be used in current environment. + A failure will be reported if the CUDA device property in the provided cache is different from current environment. + ``bool(ignore_mismatch) == True`` skips strict verification and allows loading cache created from a different device. + The cache must not be destroyed until after the engine is built. + + :arg cache: The timing cache to be used + :arg ignore_mismatch: Whether or not allow using a cache that contains different CUDA device property + + :returns: A `BOOL` indicating whether the operation is done successfully. +)trtdoc"; + +constexpr const char* get_timing_cache = R"trtdoc( + Get the timing cache from current IBuilderConfig + + :returns: The timing cache used in current IBuilderConfig, or `None` if no timing cache is set. +)trtdoc"; + +} // namespace IBuilderConfigDoc + +namespace BuilderDoc +{ +constexpr const char* descr = R"trtdoc( + Builds an :class:`ICudaEngine` from a :class:`INetworkDefinition` . + + :ivar max_batch_size: :class:`int` The maximum batch size which can be used at execution time, and also the batch size for which the :class:`ICudaEngine` will be optimized. + :ivar platform_has_tf32: :class:`bool` Whether the platform has tf32 support. + :ivar platform_has_fast_fp16: :class:`bool` Whether the platform has fast native fp16. + :ivar platform_has_fast_int8: :class:`bool` Whether the platform has fast native int8. + :ivar max_DLA_batch_size: :class:`int` The maximum batch size DLA can support. For any tensor the total volume of index dimensions combined(dimensions other than CHW) with the requested batch size should not exceed the value returned by this function. + :ivar num_DLA_cores: :class:`int` The number of DLA engines available to this builder. + :ivar error_recorder: :class:`IErrorRecorder` Application-implemented error reporting interface for TensorRT objects. + :ivar gpu_allocator: :class:`IGpuAllocator` The GPU allocator to be used by the :class:`Builder` . All GPU + memory acquired will use this allocator. If set to ``None``, the default allocator will be used. +)trtdoc"; + +constexpr const char* init = R"trtdoc( + :arg logger: The logger to use. +)trtdoc"; + +constexpr const char* create_network = R"trtdoc( + Create a :class:`INetworkDefinition` object. + + :arg flags: :class:`NetworkDefinitionCreationFlag` s combined using bitwise OR. Default value is 0. This mimics the behavior of create_network() in TensorRT 5.1. + + :returns: An empty TensorRT :class:`INetworkDefinition` . +)trtdoc"; + +constexpr const char* create_optimization_profile = R"trtdoc( + Create a new optimization profile. + + If the network has any dynamic input tensors, the appropriate calls to :func:`IOptimizationProfile.set_shape` must be made. Likewise, if there are any shape input tensors, the appropriate calls to :func:`IOptimizationProfile.set_shape_input` are required. + + See :class:`IOptimizationProfile` +)trtdoc"; + +constexpr const char* create_builder_config = R"trtdoc( + Create a builder configuration object. + + See :class:`IBuilderConfig` +)trtdoc"; + +constexpr const char* build_engine = R"trtdoc( + Builds an engine for the given :class:`INetworkDefinition` and :class:`IBuilderConfig` . + + This enables the builder to build multiple engines based on the same network definition, but with different builder configurations. + + :arg network: The TensorRT :class:`INetworkDefinition` . + :arg config: The TensorRT :class:`IBuilderConfig` . + + :returns: A new :class:`ICudaEngine` . +)trtdoc"; + +constexpr const char* build_serialized_network = R"trtdoc( + Builds and serializes a network for the given :class:`INetworkDefinition` and :class:`IBuilderConfig` . + + This function allows building and serialization of a network without creating an engine. + + :arg network: Network definition. + :arg config: Builder configuration. + + :returns: A pointer to a :class:`IHostMemory` object that contains a serialized network. +)trtdoc"; + +constexpr const char* is_network_supported = R"trtdoc( + Checks that a network is within the scope of the :class:`IBuilderConfig` settings. + + :arg network: The network definition to check for configuration compliance. + :arg config: The configuration of the builder to use when checking the network. + + Given an :class:`INetworkDefinition` and an :class:`IBuilderConfig` , check if + the network falls within the constraints of the builder configuration based on the + :class:`EngineCapability` , :class:`BuilderFlag` , and :class:`DeviceType` . + + :returns: ``True`` if the network is within the scope of the restrictions specified by the builder config, ``False`` otherwise. + This function reports the conditions that are violated to the registered :class:`ErrorRecorder` . + + NOTE: This function will synchronize the cuda stream returned by ``config.profile_stream`` before returning. + +)trtdoc"; + +} // namespace BuilderDoc + +namespace RuntimeDoc +{ +constexpr const char* descr = R"trtdoc( + Allows a serialized :class:`ICudaEngine` to be deserialized. + + :ivar error_recorder: :class:`IErrorRecorder` Application-implemented error reporting interface for TensorRT objects. + :ivar gpu_allocator: :class:`IGpuAllocator` The GPU allocator to be used by the :class:`Runtime` . All GPU memory + acquired will use this allocator. If set to None, the default allocator will be used (Default: cudaMalloc/cudaFree). +)trtdoc"; + +constexpr const char* init = R"trtdoc( + :arg logger: The logger to use. +)trtdoc"; + +constexpr const char* deserialize_cuda_engine = R"trtdoc( + Deserialize an :class:`ICudaEngine` from a stream. + + :arg serialized_engine: The :class:`buffer` that holds the serialized :class:`ICudaEngine` . + + :returns: The :class:`ICudaEngine`, or None if it could not be deserialized. +)trtdoc"; + +} // namespace RuntimeDoc + +namespace RefitterDoc +{ +constexpr const char* descr = R"trtdoc( + Updates weights in an :class:`ICudaEngine` . + + :ivar error_recorder: :class:`IErrorRecorder` Application-implemented error reporting interface for TensorRT objects. +)trtdoc"; + +constexpr const char* init = R"trtdoc( + :arg engine: The engine to refit. + :arg logger: The logger to use. +)trtdoc"; + +constexpr const char* set_weights = R"trtdoc( + Specify new weights for a layer of given name. + Possible reasons for rejection are: + + * There is no such layer by that name. + * The layer does not have weights with the specified role. + * The number of weights is inconsistent with the layer’s original specification. + + Modifying the weights before :func:`refit_cuda_engine` completes will result in undefined behavior. + + :arg layer_name: The name of the layer. + :arg role: The role of the weights. See :class:`WeightsRole` for more information. + :arg weights: The weights to refit with. + + :returns: ``True`` on success, or ``False`` if new weights are rejected. +)trtdoc"; + +constexpr const char* set_named_weights = R"trtdoc( + Specify new weights of given name. + Possible reasons for rejection are: + + * The name of weights is empty or does not correspond to any refittable weights. + * The number of weights is inconsistent with the original specification. + + Modifying the weights before method refit_cuda_engine() completes will result in undefined behavior. + + :arg name: The name of the weights to be refitted. + :arg weights: The new weights to associate with the name. + + :returns: ``True`` on success, or ``False`` if new weights are rejected. +)trtdoc"; + +constexpr const char* refit_cuda_engine = R"trtdoc( + Updates associated engine. Return ``True`` if successful. + + Failure occurs if :func:`get_missing` != 0 before the call. +)trtdoc"; + +constexpr const char* get_missing = R"trtdoc( + Get description of missing weights. + + For example, if some Weights have been set, but the engine was optimized + in a way that combines weights, any unsupplied Weights in the combination + are considered missing. + + :returns: The names of layers with missing weights, and the roles of those weights. +)trtdoc"; + +constexpr const char* get_missing_weights = R"trtdoc( + Get names of missing weights. + + For example, if some Weights have been set, but the engine was optimized + in a way that combines weights, any unsupplied Weights in the combination + are considered missing. + + :returns: The names of missing weights, empty string for unnamed weights. +)trtdoc"; + +constexpr const char* get_all = R"trtdoc( + Get description of all weights that could be refitted. + + :returns: The names of layers with refittable weights, and the roles of those weights. +)trtdoc"; + +constexpr const char* get_all_weights = R"trtdoc( + Get names of all weights that could be refitted. + + :returns: The names of refittable weights. +)trtdoc"; + +constexpr const char* get_dynamic_range = R"trtdoc( + Gets the dynamic range of a tensor. If the dynamic range was never set, returns the range computed during calibration. + + :arg tensor_name: The name of the tensor whose dynamic range to retrieve. + + :returns: :class:`Tuple[float, float]` A tuple containing the [minimum, maximum] of the dynamic range. +)trtdoc"; + +constexpr const char* set_dynamic_range = R"trtdoc( + Update dynamic range for a tensor. + + :arg tensor_name: The name of the tensor whose dynamic range to update. + :arg range: The new range. + + :returns: :class:`True` if successful, :class:`False` otherwise. + + Returns false if there is no Int8 engine tensor derived from a network tensor of that name. If successful, then :func:`get_missing` may report that some weights need to be supplied. +)trtdoc"; + +constexpr const char* get_tensors_with_dynamic_range = R"trtdoc( + Get names of all tensors that have refittable dynamic ranges. + + :returns: The names of tensors with refittable dynamic ranges. +)trtdoc"; +} // namespace RefitterDoc + +namespace AllocatorFlagDoc +{ +constexpr const char* descr = R"trtdoc()trtdoc"; + +constexpr const char* RESIZABLE = R"trtdoc(TensorRT may call realloc() on this allocation)trtdoc"; +} // namespace AllocatorFlagDoc + +namespace GpuAllocatorDoc +{ +constexpr const char* descr = R"trtdoc(Application-implemented class for controlling allocation on the GPU.)trtdoc"; + +constexpr const char* allocate = R"trtdoc( + A callback implemented by the application to handle acquisition of GPU memory. + If an allocation request of size 0 is made, ``None`` should be returned. + + If an allocation request cannot be satisfied, ``None`` should be returned. + + :arg size: The size of the memory required. + :arg alignment: The required alignment of memory. Alignment will be zero + or a power of 2 not exceeding the alignment guaranteed by cudaMalloc. + Thus this allocator can be safely implemented with cudaMalloc/cudaFree. + An alignment value of zero indicates any alignment is acceptable. + :arg flags: Allocation flags. See :class:`AllocatorFlag` + + :returns: The address of the allocated memory +)trtdoc"; + +constexpr const char* free = R"trtdoc( + A callback implemented by the application to handle release of GPU memory. + + TensorRT may pass a 0 to this function if it was previously returned by ``allocate()``. + + :arg memory: The memory address of the memory to release. +)trtdoc"; + +constexpr const char* reallocate = R"trtdoc( + A callback implemented by the application to resize an existing allocation. + + Only allocations which were allocated with AllocatorFlag.RESIZABLE will be resized. + + Options are one of: + - resize in place leaving min(old_size, new_size) bytes unchanged and return the original address + - move min(old_size, new_size) bytes to a new location of sufficient size and return its address + - return nullptr, to indicate that the request could not be fulfilled. + + If nullptr is returned, TensorRT will assume that resize() is not implemented, and that the + allocation at address is still valid. + + This method is made available for use cases where delegating the resize + strategy to the application provides an opportunity to improve memory management. + One possible implementation is to allocate a large virtual device buffer and + progressively commit physical memory with cuMemMap. CU_MEM_ALLOC_GRANULARITY_RECOMMENDED + is suggested in this case. + + TensorRT may call realloc to increase the buffer by relatively small amounts. + + :arg address: the address of the original allocation. + :arg alignment: The alignment used by the original allocation. + :arg new_size: The new memory size required. + + :returns: The address of the reallocated memory +)trtdoc"; + +} // namespace GpuAllocatorDoc + +} // namespace tensorrt diff --git a/python/docstrings/infer/pyFoundationalTypesDoc.h b/python/docstrings/infer/pyFoundationalTypesDoc.h index d3bd3735..ed697120 100644 --- a/python/docstrings/infer/pyFoundationalTypesDoc.h +++ b/python/docstrings/infer/pyFoundationalTypesDoc.h @@ -19,161 +19,157 @@ namespace tensorrt { - namespace DataTypeDoc - { - constexpr const char* descr = R"trtdoc( - Represents data types. +namespace DataTypeDoc +{ +constexpr const char* descr = R"trtdoc( + Represents data types. - :itemsize: :class:`int` The size in bytes of this :class:`DataType` . - )trtdoc"; + :itemsize: :class:`int` The size in bytes of this :class:`DataType` . +)trtdoc"; - constexpr const char* float32 = R"trtdoc(Represents a 32-bit floating point number.)trtdoc"; - constexpr const char* float16 = R"trtdoc(Represents a 16-bit floating point number.)trtdoc"; - constexpr const char* int8 = R"trtdoc(Represents an 8-bit integer.)trtdoc"; - constexpr const char* int32 = R"trtdoc(Represents a 32-bit integer.)trtdoc"; - constexpr const char* boolean = R"trtdoc(Represents a boolean.)trtdoc"; +constexpr const char* float32 = R"trtdoc(Represents a 32-bit floating point number.)trtdoc"; +constexpr const char* float16 = R"trtdoc(Represents a 16-bit floating point number.)trtdoc"; +constexpr const char* int8 = R"trtdoc(Represents an 8-bit integer.)trtdoc"; +constexpr const char* int32 = R"trtdoc(Represents a 32-bit integer.)trtdoc"; +constexpr const char* boolean = R"trtdoc(Represents a boolean.)trtdoc"; - } /* DataTypeDoc */ +} // namespace DataTypeDoc - namespace DimensionTypeDoc - { - constexpr const char* descr = R"trtdoc(The type of data encoded across this dimension.)trtdoc"; - constexpr const char* SPATIAL = R"trtdoc(Elements correspond to different spatial data.)trtdoc"; - constexpr const char* CHANNEL = R"trtdoc(Elements correspond to different channels.)trtdoc"; - constexpr const char* INDEX = R"trtdoc(Elements correspond to different batch index.)trtdoc"; - constexpr const char* SEQUENCE = R"trtdoc(Elements correspond to different sequence values.)trtdoc"; +namespace WeightsRoleDoc +{ +constexpr const char* descr + = R"trtdoc(How a layer uses particular Weights. The power weights of an IScaleLayer are omitted. Refitting those is not supported.)trtdoc"; +constexpr const char* KERNEL + = R"trtdoc(Kernel for :class:`IConvolutionLayer` , :class:`IDeconvolutionLayer` , or :class:`IFullyConnectedLayer` .)trtdoc"; +constexpr const char* BIAS + = R"trtdoc(Bias for :class:`IConvolutionLayer` , :class:`IDeconvolutionLayer` , or :class:`IFullyConnectedLayer` .)trtdoc"; +constexpr const char* SHIFT = R"trtdoc(Shift part of :class:`IScaleLayer` .)trtdoc"; +constexpr const char* SCALE = R"trtdoc(Scale part of :class:`IScaleLayer` .)trtdoc"; +constexpr const char* CONSTANT = R"trtdoc(Weights for :class:`IConstantLayer` .)trtdoc"; +constexpr const char* ANY = R"trtdoc(Any other weights role.)trtdoc"; - } /* DimensionTypeDoc */ +} // namespace WeightsRoleDoc - namespace WeightsRoleDoc - { - constexpr const char* descr = R"trtdoc(How a layer uses particular Weights. The power weights of an IScaleLayer are omitted. Refitting those is not supported.)trtdoc"; - constexpr const char* KERNEL = R"trtdoc(Kernel for :class:`IConvolutionLayer` , :class:`IDeconvolutionLayer` , or :class:`IFullyConnectedLayer` .)trtdoc"; - constexpr const char* BIAS = R"trtdoc(Bias for :class:`IConvolutionLayer` , :class:`IDeconvolutionLayer` , or :class:`IFullyConnectedLayer` .)trtdoc"; - constexpr const char* SHIFT = R"trtdoc(Shift part of :class:`IScaleLayer` .)trtdoc"; - constexpr const char* SCALE = R"trtdoc(Scale part of :class:`IScaleLayer` .)trtdoc"; - constexpr const char* CONSTANT = R"trtdoc(Weights for :class:`IConstantLayer` .)trtdoc"; +namespace WeightsDoc +{ +constexpr const char* descr = R"trtdoc( + An array of weights used as a layer parameter. + The weights are held by reference until the engine has been built - deep copies are not made automatically. - } /* WeightsRoleDoc */ + :ivar dtype: :class:`DataType` The type of the weights. + :ivar size: :class:`int` The number of weights in the array. + :ivar nbytes: :class:`int` Total bytes consumed by the elements of the weights buffer. +)trtdoc"; - namespace WeightsDoc - { - constexpr const char* descr = R"trtdoc( - An array of weights used as a layer parameter. - The weights are held by reference until the engine has been built - deep copies are not made automatically. +// FIXME: Weird bug occurring here. Cannot provide :arg: +constexpr const char* init_type = R"trtdoc( + Initializes an empty (0-length) Weights object with the specified type. - :ivar dtype: :class:`DataType` The type of the weights. - :ivar size: :class:`int` The number of weights in the array. - :ivar nbytes: :class:`int` Total bytes consumed by the elements of the weights buffer. - )trtdoc"; + :type: A type to initialize the weights with. Default: :class:`tensorrt.float32` +)trtdoc"; - constexpr const char* init_type = R"trtdoc( - Initializes an empty (0-length) Weights object with the specified type. +// FIXME: Weird bug occurring here. Cannot provide :arg: +constexpr const char* init_numpy = R"trtdoc( + :a: A numpy array whose values to use. No deep copies are made. +)trtdoc"; - :type: A type to initialize the weights with. Default: :class:`tensorrt.float32` - )trtdoc"; +constexpr const char* numpy = R"trtdoc( + Create a numpy array using the underlying buffer of this weights object. - constexpr const char* init_numpy = R"trtdoc( - :a: A numpy array whose values to use. No deep copies are made. - )trtdoc"; + :returns: A new numpy array that holds a reference to this weight object's buffer - no deep copy is made. +)trtdoc"; +} // namespace WeightsDoc - constexpr const char* numpy = R"trtdoc( - Create a numpy array using the underlying buffer of this weights object. +namespace DimsDoc +{ +constexpr const char* descr = R"trtdoc( + Structure to define the dimensions of a tensor. :class:`Dims` and all derived classes behave like Python :class:`tuple` s. Furthermore, the TensorRT API can implicitly convert Python iterables to :class:`Dims` objects, so :class:`tuple` or :class:`list` can be used in place of this class. +)trtdoc"; - :returns: A new numpy array that holds a reference to this weight object's buffer - no deep copy is made. - )trtdoc"; - } /* WeightsDoc */ +constexpr const char* volume = R"trtdoc( + Computes the total volume of the dimensions - namespace DimsDoc - { - constexpr const char* descr = R"trtdoc( - Structure to define the dimensions of a tensor. :class:`Dims` and all derived classes behave like Python :class:`tuple` s. Furthermore, the TensorRT API can implicitly convert Python iterables to :class:`Dims` objects, so :class:`tuple` or :class:`list` can be used in place of this class. - )trtdoc"; + :returns: Total volume. `0` for empty dimensions. +)trtdoc"; - constexpr const char* volume = R"trtdoc( - Computes the total volume of the dimensions +constexpr const char* get_type = R"trtdoc( + Queries the type of a dimension. - :returns: Total volume. `0` for empty dimensions. - )trtdoc"; + :returns: The type of the specified dimension. +)trtdoc"; - constexpr const char* get_type = R"trtdoc( - Queries the type of a dimension. +constexpr const char* MAX_DIMS = R"trtdoc( + The maximum number of dimensions supported by :class:`Dims`. +)trtdoc"; - :returns: The type of the specified dimension. - )trtdoc"; +} // namespace DimsDoc - constexpr const char* MAX_DIMS = R"trtdoc( - The maximum number of dimensions supported by :class:`Dims`. - )trtdoc"; +namespace Dims2Doc +{ +constexpr const char* descr = R"trtdoc( + Structure to define 2D shape. +)trtdoc"; +} // namespace Dims2Doc - } /* DimsDoc */ +namespace DimsHWDoc +{ +constexpr const char* descr = R"trtdoc( + Structure to define 2D shape with height and width. - namespace Dims2Doc - { - constexpr const char* descr = R"trtdoc( - Structure to define 2D shape. - )trtdoc"; - } /* DimsDoc */ + :ivar h: :class:`int` The first dimension (height). + :ivar w: :class:`int` The second dimension (width). +)trtdoc"; +} // namespace DimsHWDoc - namespace DimsHWDoc - { - constexpr const char* descr = R"trtdoc( - Structure to define 2D shape with height and width. +namespace Dims3Doc +{ +constexpr const char* descr = R"trtdoc( + Structure to define 3D shape. +)trtdoc"; +} // namespace Dims3Doc - :ivar h: :class:`int` The first dimension (height). - :ivar w: :class:`int` The second dimension (width). - )trtdoc"; - } /* DimsDoc */ +namespace DimsCHWDoc +{ +constexpr const char* descr = R"trtdoc( + Structure to define 3D tensor with a channel dimension, height, and width. - namespace Dims3Doc - { - constexpr const char* descr = R"trtdoc( - Structure to define 3D shape. - )trtdoc"; - } /* DimsDoc */ + :ivar c: :class:`int` The first dimension (channel). + :ivar h: :class:`int` The second dimension (height). + :ivar w: :class:`int` The third dimension (width). +)trtdoc"; +} // namespace DimsCHWDoc - namespace DimsCHWDoc - { - constexpr const char* descr = R"trtdoc( - Structure to define 3D tensor with a channel dimension, height, and width. +namespace Dims4Doc +{ +constexpr const char* descr = R"trtdoc( + Structure to define 4D tensor. +)trtdoc"; +} // namespace Dims4Doc - :ivar c: :class:`int` The first dimension (channel). - :ivar h: :class:`int` The second dimension (height). - :ivar w: :class:`int` The third dimension (width). - )trtdoc"; - } /* DimsDoc */ +namespace DimsNCHWDoc +{ +constexpr const char* descr = R"trtdoc( + Structure to define 4D tensor with a batch dimension, a channel dimension, height and width. - namespace Dims4Doc - { - constexpr const char* descr = R"trtdoc( - Structure to define 4D tensor. - )trtdoc"; - } /* DimsDoc */ + :ivar n: :class:`int` The first dimension (batch). + :ivar c: :class:`int` The second dimension (channel). + :ivar h: :class:`int` The third dimension (height). + :ivar w: :class:`int` The fourth dimension (width). +)trtdoc"; +} // namespace DimsNCHWDoc - namespace DimsNCHWDoc - { - constexpr const char* descr = R"trtdoc( - Structure to define 4D tensor with a batch dimension, a channel dimension, height and width. +namespace IHostMemoryDoc +{ +constexpr const char* descr = R"trtdoc( + Handles library allocated memory that is accessible to the user. - :ivar n: :class:`int` The first dimension (batch). - :ivar c: :class:`int` The second dimension (channel). - :ivar h: :class:`int` The third dimension (height). - :ivar w: :class:`int` The fourth dimension (width). - )trtdoc"; - } /* DimsDoc */ + The memory allocated via the host memory object is owned by the library and will be de-allocated when object is destroyed. - namespace IHostMemoryDoc - { - constexpr const char* descr = R"trtdoc( - Handles library allocated memory that is accessible to the user. + This class exposes a buffer interface using Python's buffer protocol. - The memory allocated via the host memory object is owned by the library and will be de-allocated when object is destroyed. + :ivar dtype: :class:`DataType` The data type of this buffer. + :ivar nbytes: :class:`int` Total bytes consumed by the elements of the buffer. +)trtdoc"; +} // namespace IHostMemoryDoc - This class exposes a buffer interface using Python's buffer protocol. - - :ivar dtype: :class:`DataType` The data type of this buffer. - :ivar nbytes: :class:`int` Total bytes consumed by the elements of the buffer. - )trtdoc"; - } /* IHostMemoryDoc */ - -} /* tensorrt */ +} // namespace tensorrt diff --git a/python/docstrings/infer/pyGraphDoc.h b/python/docstrings/infer/pyGraphDoc.h index 73d65582..947d9dcf 100644 --- a/python/docstrings/infer/pyGraphDoc.h +++ b/python/docstrings/infer/pyGraphDoc.h @@ -14,2027 +14,2067 @@ * limitations under the License. */ -// This file contains all INetworkDefinition related docstrings, since these are typically too long to keep in the binding code. +// This file contains all INetworkDefinition related docstrings, since these are typically too long to keep in the +// binding code. #pragma once namespace tensorrt { - namespace LayerTypeDoc - { - - constexpr const char* descr = R"trtdoc(Type of Layer)trtdoc"; - constexpr const char* CONVOLUTION = R"trtdoc(Convolution layer)trtdoc"; - constexpr const char* FULLY_CONNECTED = R"trtdoc(Fully connected layer)trtdoc"; - constexpr const char* ACTIVATION = R"trtdoc(Activation layer)trtdoc"; - constexpr const char* POOLING = R"trtdoc(Pooling layer)trtdoc"; - constexpr const char* LRN = R"trtdoc(LRN layer)trtdoc"; - constexpr const char* SCALE = R"trtdoc(Scale layer)trtdoc"; - constexpr const char* SOFTMAX = R"trtdoc(Softmax layer)trtdoc"; - constexpr const char* DECONVOLUTION = R"trtdoc(Deconvolution layer)trtdoc"; - constexpr const char* CONCATENATION = R"trtdoc(Concatenation layer)trtdoc"; - constexpr const char* ELEMENTWISE = R"trtdoc(Elementwise layer)trtdoc"; - constexpr const char* PLUGIN = R"trtdoc(Plugin layer)trtdoc"; - constexpr const char* RNN = R"trtdoc(RNN layer)trtdoc"; - constexpr const char* UNARY = R"trtdoc(Unary layer)trtdoc"; - constexpr const char* PADDING = R"trtdoc(Padding layer)trtdoc"; - constexpr const char* SHUFFLE = R"trtdoc(Shuffle layer)trtdoc"; - constexpr const char* REDUCE = R"trtdoc(Reduce layer)trtdoc"; - constexpr const char* TOPK = R"trtdoc(TopK layer)trtdoc"; - constexpr const char* GATHER = R"trtdoc(Gather layer)trtdoc"; - constexpr const char* MATRIX_MULTIPLY = R"trtdoc(Matrix multiply layer)trtdoc"; - constexpr const char* RAGGED_SOFTMAX = R"trtdoc(Ragged softmax layer)trtdoc"; - constexpr const char* CONSTANT = R"trtdoc(Constant layer)trtdoc"; - constexpr const char* RNN_V2 = R"trtdoc(RNNv2 layer)trtdoc"; - constexpr const char* IDENTITY = R"trtdoc(Identity layer)trtdoc"; - constexpr const char* PLUGIN_V2 = R"trtdoc(PluginV2 layer)trtdoc"; - constexpr const char* SLICE = R"trtdoc(Slice layer)trtdoc"; - constexpr const char* SHAPE = R"trtdoc(Shape layer)trtdoc"; - constexpr const char* PARAMETRIC_RELU = R"trtdoc(Parametric ReLU layer)trtdoc"; - constexpr const char* RESIZE = R"trtdoc(Resize layer)trtdoc"; - constexpr const char* TRIP_LIMIT = R"trtdoc(Loop Trip limit layer)trtdoc"; - constexpr const char* RECURRENCE = R"trtdoc(Loop Recurrence layer)trtdoc"; - constexpr const char* ITERATOR = R"trtdoc(Loop Iterator layer)trtdoc"; - constexpr const char* LOOP_OUTPUT = R"trtdoc(Loop output layer)trtdoc"; - constexpr const char* SELECT = R"trtdoc(Select layer)trtdoc"; - constexpr const char* FILL = R"trtdoc(Fill layer)trtdoc"; - } // LayerTypeDoc - - namespace TensorLocationDoc - { - constexpr const char* descr = R"trtdoc(The physical location of the data.)trtdoc"; - - constexpr const char* DEVICE = R"trtdoc(Data is stored on the device.)trtdoc"; - constexpr const char* HOST = R"trtdoc(Data is stored on the device.)trtdoc"; - } // TensorLocationDoc - - namespace TensorFormatDoc - { - constexpr const char* descr = R"trtdoc( - Format of the input/output tensors. +namespace LayerTypeDoc +{ + +constexpr const char* descr = R"trtdoc(Type of Layer)trtdoc"; +constexpr const char* CONVOLUTION = R"trtdoc(Convolution layer)trtdoc"; +constexpr const char* FULLY_CONNECTED = R"trtdoc(Fully connected layer)trtdoc"; +constexpr const char* ACTIVATION = R"trtdoc(Activation layer)trtdoc"; +constexpr const char* POOLING = R"trtdoc(Pooling layer)trtdoc"; +constexpr const char* LRN = R"trtdoc(LRN layer)trtdoc"; +constexpr const char* SCALE = R"trtdoc(Scale layer)trtdoc"; +constexpr const char* SOFTMAX = R"trtdoc(Softmax layer)trtdoc"; +constexpr const char* DECONVOLUTION = R"trtdoc(Deconvolution layer)trtdoc"; +constexpr const char* CONCATENATION = R"trtdoc(Concatenation layer)trtdoc"; +constexpr const char* ELEMENTWISE = R"trtdoc(Elementwise layer)trtdoc"; +constexpr const char* PLUGIN = R"trtdoc(Plugin layer)trtdoc"; +constexpr const char* UNARY = R"trtdoc(Unary layer)trtdoc"; +constexpr const char* PADDING = R"trtdoc(Padding layer)trtdoc"; +constexpr const char* SHUFFLE = R"trtdoc(Shuffle layer)trtdoc"; +constexpr const char* REDUCE = R"trtdoc(Reduce layer)trtdoc"; +constexpr const char* TOPK = R"trtdoc(TopK layer)trtdoc"; +constexpr const char* GATHER = R"trtdoc(Gather layer)trtdoc"; +constexpr const char* MATRIX_MULTIPLY = R"trtdoc(Matrix multiply layer)trtdoc"; +constexpr const char* RAGGED_SOFTMAX = R"trtdoc(Ragged softmax layer)trtdoc"; +constexpr const char* CONSTANT = R"trtdoc(Constant layer)trtdoc"; +constexpr const char* RNN_V2 = R"trtdoc(RNNv2 layer)trtdoc"; +constexpr const char* IDENTITY = R"trtdoc(Identity layer)trtdoc"; +constexpr const char* PLUGIN_V2 = R"trtdoc(PluginV2 layer)trtdoc"; +constexpr const char* SLICE = R"trtdoc(Slice layer)trtdoc"; +constexpr const char* SHAPE = R"trtdoc(Shape layer)trtdoc"; +constexpr const char* PARAMETRIC_RELU = R"trtdoc(Parametric ReLU layer)trtdoc"; +constexpr const char* RESIZE = R"trtdoc(Resize layer)trtdoc"; +constexpr const char* TRIP_LIMIT = R"trtdoc(Loop Trip limit layer)trtdoc"; +constexpr const char* RECURRENCE = R"trtdoc(Loop Recurrence layer)trtdoc"; +constexpr const char* ITERATOR = R"trtdoc(Loop Iterator layer)trtdoc"; +constexpr const char* LOOP_OUTPUT = R"trtdoc(Loop output layer)trtdoc"; +constexpr const char* SELECT = R"trtdoc(Select layer)trtdoc"; +constexpr const char* FILL = R"trtdoc(Fill layer)trtdoc"; +constexpr const char* QUANTIZE = R"trtdoc(Quantize layer)trtdoc"; +constexpr const char* DEQUANTIZE = R"trtdoc(Dequantize layer)trtdoc"; +} // namespace LayerTypeDoc - This enum is extended to be used by both plugins and reformat-free network I/O tensors. +namespace TensorLocationDoc +{ +constexpr const char* descr = R"trtdoc(The physical location of the data.)trtdoc"; - For more information about data formats, see the topic "Data Format Description" located in the - TensorRT Developer Guide (https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html). - )trtdoc"; +constexpr const char* DEVICE = R"trtdoc(Data is stored on the device.)trtdoc"; +constexpr const char* HOST = R"trtdoc(Data is stored on the device.)trtdoc"; +} // namespace TensorLocationDoc - constexpr const char* LINEAR = R"trtdoc( - Row major linear format. +namespace TensorFormatDoc +{ +constexpr const char* descr = R"trtdoc( + Format of the input/output tensors. - For a tensor with dimensions {N, C, H, W}, the W axis always has unit stride, and the stride of every other axis is at least the the product of of the next dimension times the next stride. the strides are the same as for a C array with dimensions [N][C][H][W]. - )trtdoc"; + This enum is extended to be used by both plugins and reformat-free network I/O tensors. - constexpr const char* CHW2 = R"trtdoc( - Two wide channel vectorized row major format. - - This format is bound to FP16. It is only available for dimensions >= 3. + For more information about data formats, see the topic "Data Format Description" located in the + TensorRT Developer Guide (https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html). +)trtdoc"; - For a tensor with dimensions {N, C, H, W}, the memory layout is equivalent to a C array with dimensions [N][(C+1)/2][H][W][2], with the tensor coordinates (n, c, h, w) mapping to array subscript [n][c/2][h][w][c%2]. - )trtdoc"; - - constexpr const char* HWC8 = R"trtdoc( - Eight channel format where C is padded to a multiple of 8. - - This format is bound to FP16. It is only available for dimensions >= 3. +constexpr const char* LINEAR = R"trtdoc( + Row major linear format. - For a tensor with dimensions {N, C, H, W}, the memory layout is equivalent to the array with dimensions [N][H][W][(C+7)/8*8], with the tensor coordinates (n, c, h, w) mapping to array subscript [n][h][w][c]. - )trtdoc"; + For a tensor with dimensions {N, C, H, W}, the W axis always has unit stride, and the stride of every other axis is at least the the product of of the next dimension times the next stride. the strides are the same as for a C array with dimensions [N][C][H][W]. +)trtdoc"; - constexpr const char* CHW4 = R"trtdoc( - Four wide channel vectorized row major format. - This format is bound to INT8. It is only available for dimensions >= 3. - - For a tensor with dimensions {N, C, H, W}, the memory layout is equivalent to a C array with dimensions [N][(C+3)/4][H][W][4], with the tensor coordinates (n, c, h, w) mapping to array subscript [n][c/4][h][w][c%4]. - )trtdoc"; - - constexpr const char* CHW16 = R"trtdoc( - Sixteen wide channel vectorized row major format. +constexpr const char* CHW2 = R"trtdoc( + Two wide channel vectorized row major format. - This format is bound to FP16. It is only available for dimensions >= 3. - - For a tensor with dimensions {N, C, H, W}, the memory layout is equivalent to a C array with dimensions [N][(C+15)/16][H][W][16], with the tensor coordinates (n, c, h, w) mapping to array subscript [n][c/16][h][w][c%16]. - )trtdoc"; - - constexpr const char* CHW32 = R"trtdoc( - Thirty-two wide channel vectorized row major format. - - This format is only available for dimensions >= 3. - - For a tensor with dimensions {N, C, H, W}, the memory layout is equivalent to a C array with dimensions [N][(C+31)/32][H][W][32], with the tensor coordinates (n, c, h, w) mapping to array subscript [n][c/32][h][w][c%32]. - )trtdoc"; - - constexpr const char* DHWC8 = R"trtdoc( - Eight channel format where C is padded to a multiple of 8. + This format is bound to FP16. It is only available for dimensions >= 3. - This format is bound to FP16, and it is only available for dimensions >= 4. + For a tensor with dimensions {N, C, H, W}, the memory layout is equivalent to a C array with dimensions [N][(C+1)/2][H][W][2], with the tensor coordinates (n, c, h, w) mapping to array subscript [n][c/2][h][w][c%2]. +)trtdoc"; - For a tensor with dimensions {N, C, D, H, W}, the memory layout is equivalent to an array with dimensions [N][D][H][W][(C+7)/8*8], with the tensor coordinates (n, c, d, h, w) mapping to array subscript [n][d][h][w][c]. - )trtdoc"; +constexpr const char* HWC8 = R"trtdoc( + Eight channel format where C is padded to a multiple of 8. - constexpr const char* CDHW32 = R"trtdoc( - Thirty-two wide channel vectorized row major format with 3 spatial dimensions. + This format is bound to FP16. It is only available for dimensions >= 3. - This format is bound to FP16 and INT8. It is only available for dimensions >= 4. + For a tensor with dimensions {N, C, H, W}, the memory layout is equivalent to the array with dimensions [N][H][W][(C+7)/8*8], with the tensor coordinates (n, c, h, w) mapping to array subscript [n][h][w][c]. +)trtdoc"; - For a tensor with dimensions {N, C, D, H, W}, the memory layout is equivalent to a C array with dimensions [N][(C+31)/32][D][H][W][32], with the tensor coordinates (n, d, c, h, w) mapping to array subscript [n][c/32][d][h][w][c%32]. - )trtdoc"; - - constexpr const char* HWC = R"trtdoc( - Non-vectorized channel-last format. - This format is bound to FP32 and is only available for dimensions >= 3. - )trtdoc"; +constexpr const char* CHW4 = R"trtdoc( + Four wide channel vectorized row major format. + This format is bound to INT8. It is only available for dimensions >= 3. - constexpr const char* DLA_LINEAR = R"trtdoc( - DLA planar format. Row major format. The stride for stepping along the H axis is rounded up to 64 bytes. - - This format is bound to FP16/Int8 and is only available for dimensions >= 3. + For a tensor with dimensions {N, C, H, W}, the memory layout is equivalent to a C array with dimensions [N][(C+3)/4][H][W][4], with the tensor coordinates (n, c, h, w) mapping to array subscript [n][c/4][h][w][c%4]. +)trtdoc"; - For a tensor with dimensions {N, C, H, W}, the memory layout is equivalent to a C array with dimensions [N][C][H][roundUp(W, 64/elementSize)] where elementSize is 2 for FP16 and 1 for Int8, with the tensor coordinates (n, c, h, w) mapping to array subscript [n][c][h][w]. - )trtdoc"; +constexpr const char* CHW16 = R"trtdoc( + Sixteen wide channel vectorized row major format. - constexpr const char* DLA_HWC4 = R"trtdoc( - DLA image format. channel-last format. C can only be 1, 3, 4. If C == 3 it will be rounded to 4. The stride for stepping along the H axis is rounded up to 32 bytes. - - This format is bound to FP16/Int8 and is only available for dimensions >= 3. + This format is bound to FP16. It is only available for dimensions >= 3. - For a tensor with dimensions {N, C, H, W}, with C’ is 1, 4, 4 when C is 1, 3, 4 respectively, the memory layout is equivalent to a C array with dimensions [N][H][roundUp(W, 32/C'/elementSize)][C'] where elementSize is 2 for FP16 and 1 for Int8, C' is the rounded C. The tensor coordinates (n, c, h, w) maps to array subscript [n][h][w][c]. - )trtdoc"; + For a tensor with dimensions {N, C, H, W}, the memory layout is equivalent to a C array with dimensions [N][(C+15)/16][H][W][16], with the tensor coordinates (n, c, h, w) mapping to array subscript [n][c/16][h][w][c%16]. +)trtdoc"; - } // TensorFormatDoc +constexpr const char* CHW32 = R"trtdoc( + Thirty-two wide channel vectorized row major format. + This format is only available for dimensions >= 3. - namespace ITensorDoc - { - constexpr const char* descr = R"trtdoc( - A tensor in an :class:`INetworkDefinition` . + For a tensor with dimensions {N, C, H, W}, the memory layout is equivalent to a C array with dimensions [N][(C+31)/32][H][W][32], with the tensor coordinates (n, c, h, w) mapping to array subscript [n][c/32][h][w][c%32]. +)trtdoc"; - :ivar name: :class:`str` The tensor name. For a network input, the name is assigned by the application. For tensors which are layer outputs, a default name is assigned consisting of the layer name followed by the index of the output in brackets. +constexpr const char* DHWC8 = R"trtdoc( + Eight channel format where C is padded to a multiple of 8. - :ivar shape: :class:`Dims` The shape of a tensor. For a network input the shape is assigned by the application. For a network output it is computed based on the layer parameters and the inputs to the layer. If a tensor size or a parameter is modified in the network, the shape of all dependent tensors will be recomputed. This call is only legal for network input tensors, since the shape of layer output tensors are inferred based on layer inputs and parameters. + This format is bound to FP16, and it is only available for dimensions >= 4. - :ivar dtype: :class:`DataType` The data type of a tensor. The type is unchanged if the type is invalid for the given tensor. + For a tensor with dimensions {N, C, D, H, W}, the memory layout is equivalent to an array with dimensions [N][D][H][W][(C+7)/8*8], with the tensor coordinates (n, c, d, h, w) mapping to array subscript [n][d][h][w][c]. +)trtdoc"; - :ivar broadcast_across_batch: :class:`bool` Whether to enable broadcast of tensor across the batch. When a tensor is broadcast across a batch, it has the same value for every member in the batch. Memory is only allocated once for the single member. This method is only valid for network input tensors, since the flags of layer output tensors are inferred based on layer inputs and parameters. If this state is modified for a tensor in the network, the states of all dependent tensors will be recomputed. +constexpr const char* CDHW32 = R"trtdoc( + Thirty-two wide channel vectorized row major format with 3 spatial dimensions. - :ivar location: :class:`TensorLocation` The storage location of a tensor. - :ivar is_network_input: :class:`bool` Whether the tensor is a network input. - :ivar is_network_output: :class:`bool` Whether the tensor is a network output. - :ivar dynamic_range: :class:`Tuple[float, float]` A tuple containing the [minimum, maximum] of the dynamic range, or :class:`None` if the range was not set. - :ivar is_shape: :class:`bool` Whether the tensor is a shape tensor. - :ivar allowed_formats: :class:`int` The allowed set of TensorFormat candidates. This should be an integer consisting of one or more :class:`TensorFormat` s, combined via bitwise OR after bit shifting. For example, ``1 << int(TensorFormats.CHW4) | 1 << int(TensorFormat.CHW32)``. - )trtdoc"; + This format is bound to FP16 and INT8. It is only available for dimensions >= 4. - constexpr const char* set_dynamic_range = R"trtdoc( - Set dynamic range for the tensor. - NOTE: It is suggested to use ``tensor.dynamic_range = (min, max)`` instead. + For a tensor with dimensions {N, C, D, H, W}, the memory layout is equivalent to a C array with dimensions [N][(C+31)/32][D][H][W][32], with the tensor coordinates (n, d, c, h, w) mapping to array subscript [n][c/32][d][h][w][c%32]. +)trtdoc"; - :arg min: Minimum of the dynamic range. - :arg max: Maximum of the dyanmic range. - :returns: true if succeed in setting range. Otherwise false. - )trtdoc"; +constexpr const char* HWC = R"trtdoc( + Non-vectorized channel-last format. + This format is bound to FP32 and is only available for dimensions >= 3. +)trtdoc"; - constexpr const char* get_dynamic_range = R"trtdoc( - Get dynamic range for the tensor. - NOTE: It is suggested to use ``tensor.dynamic_range`` instead, which is a tuple including both the minimum and maximum of the dynamic range. +constexpr const char* DLA_LINEAR = R"trtdoc( + DLA planar format. Row major format. The stride for stepping along the H axis is rounded up to 64 bytes. - :returns: The absolute maximum of the dynamic range. - )trtdoc"; + This format is bound to FP16/Int8 and is only available for dimensions >= 3. - constexpr const char* reset_dynamic_range = R"trtdoc( - Undo the effect of setting the dynamic range. - )trtdoc"; - } // ITensorDoc + For a tensor with dimensions {N, C, H, W}, the memory layout is equivalent to a C array with dimensions [N][C][H][roundUp(W, 64/elementSize)] where elementSize is 2 for FP16 and 1 for Int8, with the tensor coordinates (n, c, h, w) mapping to array subscript [n][c][h][w]. +)trtdoc"; - namespace ILayerDoc - { - constexpr const char* descr = R"trtdoc( - Base class for all layer classes in an :class:`INetworkDefinition` . +constexpr const char* DLA_HWC4 = R"trtdoc( + DLA image format. channel-last format. C can only be 1, 3, 4. If C == 3 it will be rounded to 4. The stride for stepping along the H axis is rounded up to 32 bytes. - :ivar name: :class:`str` The name of the layer. - :ivar type: :class:`LayerType` The type of the layer. - :ivar num_inputs: :class:`int` The number of inputs of the layer. - :ivar num_outputs: :class:`int` The number of outputs of the layer. - :ivar precision: :class:`DataType` The computation precision. - :ivar precision_is_set: :class:`bool` Whether the precision is set or not. - )trtdoc"; + This format is bound to FP16/Int8 and is only available for dimensions >= 3. - constexpr const char* set_input = R"trtdoc( - Set the layer input corresponding to the given index. + For a tensor with dimensions {N, C, H, W}, with C’ is 1, 4, 4 when C is 1, 3, 4 respectively, the memory layout is equivalent to a C array with dimensions [N][H][roundUp(W, 32/C'/elementSize)][C'] where elementSize is 2 for FP16 and 1 for Int8, C' is the rounded C. The tensor coordinates (n, c, h, w) maps to array subscript [n][h][w][c]. +)trtdoc"; - :arg index: The index of the input tensor. - :arg tensor: The input tensor. - )trtdoc"; +constexpr const char* HWC16 = R"trtdoc( + Sixteen channel format where C is padded to a multiple of 16. This format is bound to FP16. It is only available for dimensions >= 3. + For a tensor with dimensions {N, C, H, W}, the memory layout is equivalent to the array with dimensions [N][H][W][(C+15)/16*16], with the tensor coordinates (n, c, h, w) mapping to array subscript [n][h][w][c]. +)trtdoc"; - constexpr const char* get_input = R"trtdoc( - Get the layer input corresponding to the given index. +} // namespace TensorFormatDoc - :arg index: The index of the input tensor. +namespace ITensorDoc +{ +constexpr const char* descr = R"trtdoc( + A tensor in an :class:`INetworkDefinition` . - :returns: The input tensor, or :class:`None` if the index is out of range. - )trtdoc"; + :ivar name: :class:`str` The tensor name. For a network input, the name is assigned by the application. For tensors which are layer outputs, a default name is assigned consisting of the layer name followed by the index of the output in brackets. - constexpr const char* get_output = R"trtdoc( - Get the layer output corresponding to the given index. + :ivar shape: :class:`Dims` The shape of a tensor. For a network input the shape is assigned by the application. For a network output it is computed based on the layer parameters and the inputs to the layer. If a tensor size or a parameter is modified in the network, the shape of all dependent tensors will be recomputed. This call is only legal for network input tensors, since the shape of layer output tensors are inferred based on layer inputs and parameters. - :arg index: The index of the output tensor. + :ivar dtype: :class:`DataType` The data type of a tensor. The type is unchanged if the type is invalid for the given tensor. - :returns: The output tensor, or :class:`None` if the index is out of range. - )trtdoc"; + :ivar broadcast_across_batch: :class:`bool` Whether to enable broadcast of tensor across the batch. When a tensor is broadcast across a batch, it has the same value for every member in the batch. Memory is only allocated once for the single member. This method is only valid for network input tensors, since the flags of layer output tensors are inferred based on layer inputs and parameters. If this state is modified for a tensor in the network, the states of all dependent tensors will be recomputed. - constexpr const char* reset_precision = R"trtdoc( - Reset the computation precision of the layer. - )trtdoc"; + :ivar location: :class:`TensorLocation` The storage location of a tensor. + :ivar is_network_input: :class:`bool` Whether the tensor is a network input. + :ivar is_network_output: :class:`bool` Whether the tensor is a network output. + :ivar dynamic_range: :class:`Tuple[float, float]` A tuple containing the [minimum, maximum] of the dynamic range, or :class:`None` if the range was not set. + :ivar is_shape: :class:`bool` Whether the tensor is a shape tensor. + :ivar allowed_formats: :class:`int` The allowed set of TensorFormat candidates. This should be an integer consisting of one or more :class:`TensorFormat` s, combined via bitwise OR after bit shifting. For example, ``1 << int(TensorFormats.CHW4) | 1 << int(TensorFormat.CHW32)``. +)trtdoc"; - constexpr const char* set_output_type = R"trtdoc( - Constraint layer to generate output data with given type. - Note that this method cannot be used to set the data type - of the second output tensor of the topK layer. The data - type of the second output tensor of the topK layer is always Int32. +constexpr const char* set_dynamic_range = R"trtdoc( + Set dynamic range for the tensor. + NOTE: It is suggested to use ``tensor.dynamic_range = (min, max)`` instead. - :arg index: The index of the output tensor to set the type. - :arg dtype: DataType of the output. - )trtdoc"; + :arg min: Minimum of the dynamic range. + :arg max: Maximum of the dyanmic range. + :returns: true if succeed in setting range. Otherwise false. +)trtdoc"; - constexpr const char* get_output_type = R"trtdoc( - Get the output type of the layer. +constexpr const char* get_dynamic_range = R"trtdoc( + Get dynamic range for the tensor. + NOTE: It is suggested to use ``tensor.dynamic_range`` instead, which is a tuple including both the minimum and maximum of the dynamic range. - :arg index: The index of the output tensor. + :returns: The absolute maximum of the dynamic range. +)trtdoc"; - :returns: The output precision. Default : DataType.FLOAT. - )trtdoc"; +constexpr const char* reset_dynamic_range = R"trtdoc( + Undo the effect of setting the dynamic range. +)trtdoc"; +} // namespace ITensorDoc - constexpr const char* output_type_is_set = R"trtdoc( - Whether the output type has been set for this layer. +namespace ILayerDoc +{ +constexpr const char* descr = R"trtdoc( + Base class for all layer classes in an :class:`INetworkDefinition` . - :arg index: The index of the output. + :ivar name: :class:`str` The name of the layer. + :ivar type: :class:`LayerType` The type of the layer. + :ivar num_inputs: :class:`int` The number of inputs of the layer. + :ivar num_outputs: :class:`int` The number of outputs of the layer. + :ivar precision: :class:`DataType` The computation precision. + :ivar precision_is_set: :class:`bool` Whether the precision is set or not. +)trtdoc"; - :returns: Whether the output type has been explicitly set. - )trtdoc"; +constexpr const char* set_input = R"trtdoc( + Set the layer input corresponding to the given index. - constexpr const char* reset_output_type = R"trtdoc( - Reset output type of this layer. + :arg index: The index of the input tensor. + :arg tensor: The input tensor. +)trtdoc"; - :arg index: The index of the output. - )trtdoc"; - - } // ILayerDoc +constexpr const char* get_input = R"trtdoc( + Get the layer input corresponding to the given index. - namespace PaddingModeDoc - { - constexpr const char* descr = R"trtdoc( - Enumerates types of padding available in convolution, deconvolution and pooling layers. - Padding mode takes precedence if both :attr:`padding_mode` and :attr:`pre_padding` are set. + :arg index: The index of the input tensor. - | EXPLICIT* corresponds to explicit padding. - | SAME* implicitly calculates padding such that the output dimensions are the same as the input dimensions. For convolution and pooling, - output dimensions are determined by ceil(input dimensions, stride). - | CAFFE* corresponds to symmetric padding. - )trtdoc"; + :returns: The input tensor, or :class:`None` if the index is out of range. +)trtdoc"; - constexpr const char* EXPLICIT_ROUND_DOWN = R"trtdoc(Use explicit padding, rounding the output size down)trtdoc"; - constexpr const char* EXPLICIT_ROUND_UP = R"trtdoc(Use explicit padding, rounding the output size up)trtdoc"; - constexpr const char* SAME_UPPER = R"trtdoc(Use SAME padding, with :attr:`pre_padding` <= :attr:`post_padding` )trtdoc"; - constexpr const char* SAME_LOWER = R"trtdoc(Use SAME padding, with :attr:`pre_padding` >= :attr:`post_padding` )trtdoc"; - constexpr const char* CAFFE_ROUND_DOWN = R"trtdoc(Use CAFFE padding, rounding the output size down)trtdoc"; - constexpr const char* CAFFE_ROUND_UP = R"trtdoc(Use CAFFE padding, rounding the output size up)trtdoc"; +constexpr const char* get_output = R"trtdoc( + Get the layer output corresponding to the given index. - } // PaddingModeDoc + :arg index: The index of the output tensor. - namespace IConvolutionLayerDoc - { - constexpr const char* descr = R"trtdoc( - A convolution layer in an :class:`INetworkDefinition` . - - This layer performs a correlation operation between 3-dimensional filter with a 4-dimensional tensor to produce another 4-dimensional tensor. - - An optional bias argument is supported, which adds a per-channel constant to each value in the output. - - :ivar kernel_size: :class:`DimsHW` The HW kernel size of the convolution. - :ivar num_output_maps: :class:`int` The number of output maps for the convolution. - :ivar stride: :class:`DimsHW` The stride of the convolution. Default: (1, 1) - :ivar padding: :class:`DimsHW` The padding of the convolution. The input will be zero-padded by this number of elements in the height and width directions. If the padding is asymmetric, this value corresponds to the pre-padding. Default: (0, 0) - :ivar pre_padding: :class:`DimsHW` The pre-padding. The start of input will be zero-padded by this number of elements in the height and width directions. Default: (0, 0) - :ivar post_padding: :class:`DimsHW` The post-padding. The end of input will be zero-padded by this number of elements in the height and width directions. Default: (0, 0) - :ivar padding_mode: :class:`PaddingMode` The padding mode. Padding mode takes precedence if both :attr:`IConvolutionLayer.padding_mode` and either :attr:`IConvolutionLayer.pre_padding` or :attr:`IConvolutionLayer.post_padding` are set. - :ivar num_groups: :class:`int` The number of groups for a convolution. The input tensor channels are divided into this many groups, and a convolution is executed for each group, using a filter per group. The results of the group convolutions are concatenated to form the output. **Note** When using groups in int8 mode, the size of the groups (i.e. the channel count divided by the group count) must be a multiple of 4 for both input and output. Default: 1. - :ivar kernel: :class:`Weights` The kernel weights for the convolution. The weights are specified as a contiguous array in `GKCRS` order, where `G` is the number of groups, `K` the number of output feature maps, `C` the number of input channels, and `R` and `S` are the height and width of the filter. - :ivar bias: :class:`Weights` The bias weights for the convolution. Bias is optional. To omit bias, set this to an empty :class:`Weights` object. The bias is applied per-channel, so the number of weights (if non-zero) must be equal to the number of output feature maps. - :ivar dilation: :class:`DimsHW` The dilation for a convolution. Default: (1, 1) - :ivar kernel_size_nd: :class:`Dims` The multi-dimension kernel size of the convolution. - :ivar stride_nd: :class:`Dims` The multi-dimension stride of the convolution. Default: (1, ..., 1) - :ivar padding_nd: :class:`Dims` The multi-dimension padding of the convolution. The input will be zero-padded by this number of elements in each dimension. If the padding is asymmetric, this value corresponds to the pre-padding. Default: (0, ..., 0) - :ivar dilation_nd: :class:`Dims` The multi-dimension dilation for the convolution. Default: (1, ..., 1) - )trtdoc"; - } // IConvolutionLayerDoc - - namespace IFullyConnectedLayerDoc - { - constexpr const char* descr = R"trtdoc( - A fully connected layer in an :class:`INetworkDefinition` . - - This layer expects an input tensor of three or more non-batch dimensions. The input is automatically reshaped into an `MxV` tensor `X`, where `V` is a product of the last three dimensions and `M` is a product of the remaining dimensions (where the product over 0 dimensions is defined as 1). For example: - - - If the input tensor has shape `{C, H, W}`, then the tensor is reshaped into `{1, C*H*W}` . - - If the input tensor has shape `{P, C, H, W}`, then the tensor is reshaped into `{P, C*H*W}` . - - The layer then performs: - - :math:`Y := matmul(X, W^T) + bias` - - Where `X` is the `MxV` tensor defined above, `W` is the `KxV` weight tensor of the layer, and `bias` is a row vector size `K` that is broadcasted to `MxK` . `K` is the number of output channels, and configurable via :attr:`IFullyConnectedLayer.num_output_channels` . If `bias` is not specified, it is implicitly `0` . - - The `MxK` result `Y` is then reshaped such that the last three dimensions are `{K, 1, 1}` and the remaining dimensions match the dimensions of the input tensor. For example: - - - If the input tensor has shape `{C, H, W}`, then the output tensor will have shape `{K, 1, 1}` . - - If the input tensor has shape `{P, C, H, W}`, then the output tensor will have shape `{P, K, 1, 1}` . - - :ivar num_output_channels: :class:`int` The number of output channels `K` from the fully connected layer. - :ivar kernel: :class:`Weights` The kernel weights, given as a `KxC` matrix in row-major order. - :ivar bias: :class:`Weights` The bias weights. Bias is optional. To omit bias, set this to an empty :class:`Weights` object. - )trtdoc"; - } // IFullyConnectedLayerDoc - - namespace ActivationTypeDoc - { - constexpr const char* descr = R"trtdoc(The type of activation to perform.)trtdoc"; - - constexpr const char* RELU = R"trtdoc(Rectified Linear activation)trtdoc"; - constexpr const char* SIGMOID = R"trtdoc(Sigmoid activation)trtdoc"; - constexpr const char* TANH = R"trtdoc(Hyperbolic Tangent activation)trtdoc"; - constexpr const char* LEAKY_RELU = R"trtdoc(Leaky Relu activation: f(x) = x if x >= 0, f(x) = alpha * x if x < 0)trtdoc"; - constexpr const char* ELU = R"trtdoc(Elu activation: f(x) = x if x >= 0, f(x) = alpha * (exp(x) - 1) if x < 0)trtdoc"; - constexpr const char* SELU = R"trtdoc(Selu activation: f(x) = beta * x if x > 0, f(x) = beta * (alpha * exp(x) - alpha) if x <= 0)trtdoc"; - constexpr const char* SOFTSIGN = R"trtdoc(Softsign activation: f(x) = x / (1 + abs(x)))trtdoc"; - constexpr const char* SOFTPLUS = R"trtdoc(Softplus activation: f(x) = alpha * log(exp(beta * x) + 1))trtdoc"; - constexpr const char* CLIP = R"trtdoc(Clip activation: f(x) = max(alpha, min(beta, x)))trtdoc"; - constexpr const char* HARD_SIGMOID = R"trtdoc(Hard sigmoid activation: f(x) = max(0, min(1, alpha * x + beta)))trtdoc"; - constexpr const char* SCALED_TANH = R"trtdoc(Scaled Tanh activation: f(x) = alpha * tanh(beta * x))trtdoc"; - constexpr const char* THRESHOLDED_RELU = R"trtdoc(Thresholded Relu activation: f(x) = x if x > alpha, f(x) = 0 if x <= alpha)trtdoc"; - - } // ActivationTypeDoc - - namespace IActivationLayerDoc - { - constexpr const char* descr = R"trtdoc( - An Activation layer in an :class:`INetworkDefinition` . This layer applies a per-element activation function to its input. The output has the same shape as the input. - - :ivar type: :class:`ActivationType` The type of activation to be performed. - :ivar alpha: :class:`float` The alpha parameter that is used by some parametric activations (LEAKY_RELU, ELU, SELU, SOFTPLUS, CLIP, HARD_SIGMOID, SCALED_TANH). Other activations ignore this parameter. - :ivar beta: :class:`float` The beta parameter that is used by some parametric activations (SELU, SOFTPLUS, CLIP, HARD_SIGMOID, SCALED_TANH). Other activations ignore this parameter. - )trtdoc"; - } // IActivationLayerDoc - - namespace PoolingTypeDoc - { - constexpr const char* descr = R"trtdoc(The type of pooling to perform in a pooling layer.)trtdoc"; - - constexpr const char* MAX = R"trtdoc(Maximum over elements)trtdoc"; - constexpr const char* AVERAGE = R"trtdoc(Average over elements. If the tensor is padded, the count includes the padding)trtdoc"; - constexpr const char* MAX_AVERAGE_BLEND = R"trtdoc(Blending between the max pooling and average pooling: `(1-blendFactor)*maxPool + blendFactor*avgPool`)trtdoc"; - } // PoolingTypeDoc - - - namespace IPoolingLayerDoc - { - constexpr const char* descr = R"trtdoc( - A Pooling layer in an :class:`INetworkDefinition` . The layer applies a reduction operation within a window over the input. - - :ivar type: :class:`PoolingType` The type of pooling to be performed. - :ivar window_size: :class:`DimsHW` The window size for pooling. - :ivar stride: :class:`DimsHW` The stride for pooling. Default: (1, 1) - :ivar padding: :class:`DimsHW` The padding for pooling. Default: (0, 0) - :ivar pre_padding: :class:`DimsHW` The pre-padding. The start of input will be zero-padded by this number of elements in the height and width directions. Default: (0, 0) - :ivar post_padding: :class:`DimsHW` The post-padding. The end of input will be zero-padded by this number of elements in the height and width directions. Default: (0, 0) - :ivar padding_mode: :class:`PaddingMode` The padding mode. Padding mode takes precedence if both :attr:`IPoolingLayer.padding_mode` and either :attr:`IPoolingLayer.pre_padding` or :attr:`IPoolingLayer.post_padding` are set. - :ivar blend_factor: :class:`float` The blending factor for the max_average_blend mode: :math:`max_average_blendPool = (1-blendFactor)*maxPool + blendFactor*avgPool` . ``blend_factor`` is a user value in [0,1] with the default value of 0.0. This value only applies for the :const:`PoolingType.MAX_AVERAGE_BLEND` mode. - :ivar average_count_excludes_padding: :class:`bool` Whether average pooling uses as a denominator the overlap area between the window and the unpadded input. If this is not set, the denominator is the overlap between the pooling window and the padded input. Default: True - :ivar window_size_nd: :class:`Dims` The multi-dimension window size for pooling. - :ivar stride_nd: :class:`Dims` The multi-dimension stride for pooling. Default: (1, ..., 1) - :ivar padding_nd: :class:`Dims` The multi-dimension padding for pooling. Default: (0, ..., 0) - )trtdoc"; - } // IPoolingLayerDoc - - namespace ILRNLayerDoc - { - constexpr const char* descr = R"trtdoc( - A LRN layer in an :class:`INetworkDefinition` . The output size is the same as the input size. - - :ivar window_size: :class:`int` The LRN window size. The window size must be odd and in the range of [1, 15]. - :ivar alpha: :class:`float` The LRN alpha value. The valid range is [-1e20, 1e20]. - :ivar beta: :class:`float` The LRN beta value. The valid range is [0.01, 1e5f]. - :ivar k: :class:`float` The LRN K value. The valid range is [1e-5, 1e10]. - )trtdoc"; - } // ILRNLayerDoc - - namespace ScaleModeDoc - { - constexpr const char* descr = R"trtdoc(Controls how scale is applied in a Scale layer.)trtdoc"; - - constexpr const char* UNIFORM = R"trtdoc(Identical coefficients across all elements of the tensor.)trtdoc"; - constexpr const char* CHANNEL = R"trtdoc(Per-channel coefficients. The channel dimension is assumed to be the third to last dimension.)trtdoc"; - constexpr const char* ELEMENTWISE = R"trtdoc(Elementwise coefficients.)trtdoc"; - } // ScaleModeDoc - - - namespace IScaleLayerDoc - { - constexpr const char* descr = R"trtdoc( - A Scale layer in an :class:`INetworkDefinition` . - - This layer applies a per-element computation to its input: - - :math:`output = (input * scale + shift) ^ power` - - The coefficients can be applied on a per-tensor, per-channel, or per-element basis. - - **Note** - If the number of weights is 0, then a default value is used for shift, power, and scale. The default shift is 0, the default power is 1, and the default scale is 1. - - The output size is the same as the input size. - - **Note** - The input tensor for this layer is required to have a minimum of 3 dimensions. - - :ivar mode: :class:`ScaleMode` The scale mode. - :ivar shift: :class:`Weights` The shift value. - :ivar scale: :class:`Weights` The scale value. - :ivar power: :class:`Weights` The power value. - :ivar channel_axis: :class:`int` The channel axis. - )trtdoc"; - } // IScaleLayerDoc - - - namespace ISoftMaxLayerDoc - { - constexpr const char* descr = R"trtdoc( - A Softmax layer in an :class:`INetworkDefinition` . - - This layer applies a per-channel softmax to its input. - - The output size is the same as the input size. - - :ivar axes: :class:`int` The axes along which softmax is computed. Currently, only one axis can be set. The axis is specified by setting the bit corresponding to the axis, after excluding the batch dimension, to 1. Let's say we have an NCHW tensor as input (three non-batch dimensions). Bit 0 corresponds to the C dimension boolean. Bit 1 corresponds to the H dimension boolean. Bit 2 corresponds to the W dimension boolean. For example, to perform softmax on axis R of a NPQRCHW input, set bit 2. By default, softmax is performed on the axis which is the number of non-batch axes minus three. It is 0 if there are fewer than 3 non-batch axes. For example, if the input is NCHW, the default axis is C. If the input is NHW, then the default axis is H. - )trtdoc"; - } // ISoftMaxLayerDoc - - - namespace IConcatenationLayerDoc - { - constexpr const char* descr = R"trtdoc( - A concatenation layer in an :class:`INetworkDefinition` . - - The output channel size is the sum of the channel sizes of the inputs. - The other output sizes are the same as the other input sizes, which must all match. - - :ivar axis: :class:`int` The axis along which concatenation occurs. 0 is the major axis (excluding the batch dimension). The default is the number of non-batch axes in the tensor minus three (e.g. for an NCHW input it would be 0), or 0 if there are fewer than 3 non-batch axes. - )trtdoc"; - } // IConcatenationLayerDoc - - namespace IDeconvolutionLayerDoc - { - constexpr const char* descr = R"trtdoc( - A deconvolution layer in an :class:`INetworkDefinition` . - - :ivar kernel_size: :class:`DimsHW` The HW kernel size of the convolution. - :ivar num_output_maps: :class:`int` The number of output feature maps for the deconvolution. - :ivar stride: :class:`DimsHW` The stride of the deconvolution. Default: (1, 1) - :ivar padding: :class:`DimsHW` The padding of the deconvolution. The input will be zero-padded by this number of elements in the height and width directions. Padding is symmetric. Default: (0, 0) - :ivar pre_padding: :class:`DimsHW` The pre-padding. The start of input will be zero-padded by this number of elements in the height and width directions. Default: (0, 0) - :ivar post_padding: :class:`DimsHW` The post-padding. The end of input will be zero-padded by this number of elements in the height and width directions. Default: (0, 0) - :ivar padding_mode: :class:`PaddingMode` The padding mode. Padding mode takes precedence if both :attr:`IDeconvolutionLayer.padding_mode` and either :attr:`IDeconvolutionLayer.pre_padding` or :attr:`IDeconvolutionLayer.post_padding` are set. - :ivar num_groups: :class:`int` The number of groups for a deconvolution. The input tensor channels are divided into this many groups, and a deconvolution is executed for each group, using a filter per group. The results of the group convolutions are concatenated to form the output. **Note** When using groups in int8 mode, the size of the groups (i.e. the channel count divided by the group count) must be a multiple of 4 for both input and output. Default: 1 - :ivar kernel: :class:`Weights` The kernel weights for the deconvolution. The weights are specified as a contiguous array in `CKRS` order, where `C` the number of input channels, `K` the number of output feature maps, and `R` and `S` are the height and width of the filter. - :ivar bias: :class:`Weights` The bias weights for the deconvolution. Bias is optional. To omit bias, set this to an empty :class:`Weights` object. The bias is applied per-feature-map, so the number of weights (if non-zero) must be equal to the number of output feature maps. - :ivar kernel_size_nd: :class:`Dims` The multi-dimension kernel size of the convolution. - :ivar stride_nd: :class:`Dims` The multi-dimension stride of the deconvolution. Default: (1, ..., 1) - :ivar padding_nd: :class:`Dims` The multi-dimension padding of the deconvolution. The input will be zero-padded by this number of elements in each dimension. Padding is symmetric. Default: (0, ..., 0) - )trtdoc"; - } // IDeconvolutionLayerDoc + :returns: The output tensor, or :class:`None` if the index is out of range. +)trtdoc"; +constexpr const char* reset_precision = R"trtdoc( + Reset the computation precision of the layer. +)trtdoc"; - namespace ElementWiseOperationDoc - { - constexpr const char* descr = R"trtdoc(The binary operations that may be performed by an ElementWise layer.)trtdoc"; +constexpr const char* set_output_type = R"trtdoc( + Constraint layer to generate output data with given type. + Note that this method cannot be used to set the data type + of the second output tensor of the topK layer. The data + type of the second output tensor of the topK layer is always Int32. - constexpr const char* SUM = R"trtdoc(Sum of the two elements)trtdoc"; - constexpr const char* PROD = R"trtdoc(Product of the two elements)trtdoc"; - constexpr const char* MAX = R"trtdoc(Max of the two elements)trtdoc"; - constexpr const char* MIN = R"trtdoc(Min of the two elements)trtdoc"; - constexpr const char* SUB = R"trtdoc(Subtract the second element from the first)trtdoc"; - constexpr const char* DIV = R"trtdoc(Divide the first element by the second)trtdoc"; - constexpr const char* POW = R"trtdoc(The first element to the power of the second element)trtdoc"; - constexpr const char* FLOOR_DIV = R"trtdoc(Floor division of the first element by the second)trtdoc"; - constexpr const char* AND = R"trtdoc(Logical AND of two elements)trtdoc"; - constexpr const char* OR = R"trtdoc(Logical OR of two elements)trtdoc"; - constexpr const char* XOR = R"trtdoc(Logical XOR of two elements)trtdoc"; - constexpr const char* EQUAL = R"trtdoc(Check if two elements are equal)trtdoc"; - constexpr const char* GREATER = R"trtdoc(Check if element in first tensor is greater than corresponding element in second tensor)trtdoc"; - constexpr const char* LESS = R"trtdoc(Check if element in first tensor is less than corresponding element in second tensor)trtdoc"; - } // ElementWiseOperationDoc + :arg index: The index of the output tensor to set the type. + :arg dtype: DataType of the output. +)trtdoc"; +constexpr const char* get_output_type = R"trtdoc( + Get the output type of the layer. - namespace IElementWiseLayerDoc - { - constexpr const char* descr = R"trtdoc( - A elementwise layer in an :class:`INetworkDefinition` . + :arg index: The index of the output tensor. - This layer applies a per-element binary operation between corresponding elements of two tensors. + :returns: The output precision. Default : DataType.FLOAT. +)trtdoc"; - The input dimensions of the two input tensors must be equal, and the output tensor is the same size as each input. +constexpr const char* output_type_is_set = R"trtdoc( + Whether the output type has been set for this layer. - :ivar op: :class:`ElementWiseOperation` The binary operation for the layer. - )trtdoc"; - } // IElementWiseLayerDoc + :arg index: The index of the output. - namespace IGatherLayerDoc - { - constexpr const char* descr = R"trtdoc( - A gather layer in an :class:`INetworkDefinition` . + :returns: Whether the output type has been explicitly set. +)trtdoc"; + +constexpr const char* reset_output_type = R"trtdoc( + Reset output type of this layer. + + :arg index: The index of the output. +)trtdoc"; + +} // namespace ILayerDoc + +namespace PaddingModeDoc +{ +constexpr const char* descr = R"trtdoc( + Enumerates types of padding available in convolution, deconvolution and pooling layers. + Padding mode takes precedence if both :attr:`padding_mode` and :attr:`pre_padding` are set. + + | EXPLICIT* corresponds to explicit padding. + | SAME* implicitly calculates padding such that the output dimensions are the same as the input dimensions. For convolution and pooling, + output dimensions are determined by ceil(input dimensions, stride). + | CAFFE* corresponds to symmetric padding. +)trtdoc"; + +constexpr const char* EXPLICIT_ROUND_DOWN = R"trtdoc(Use explicit padding, rounding the output size down)trtdoc"; +constexpr const char* EXPLICIT_ROUND_UP = R"trtdoc(Use explicit padding, rounding the output size up)trtdoc"; +constexpr const char* SAME_UPPER = R"trtdoc(Use SAME padding, with :attr:`pre_padding` <= :attr:`post_padding` )trtdoc"; +constexpr const char* SAME_LOWER = R"trtdoc(Use SAME padding, with :attr:`pre_padding` >= :attr:`post_padding` )trtdoc"; +constexpr const char* CAFFE_ROUND_DOWN = R"trtdoc(Use CAFFE padding, rounding the output size down)trtdoc"; +constexpr const char* CAFFE_ROUND_UP = R"trtdoc(Use CAFFE padding, rounding the output size up)trtdoc"; + +} // namespace PaddingModeDoc + +namespace IConvolutionLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A convolution layer in an :class:`INetworkDefinition` . + + This layer performs a correlation operation between 3-dimensional filter with a 4-dimensional tensor to produce another 4-dimensional tensor. + + An optional bias argument is supported, which adds a per-channel constant to each value in the output. + + :ivar kernel_size: :class:`DimsHW` The HW kernel size of the convolution. + :ivar num_output_maps: :class:`int` The number of output maps for the convolution. + :ivar stride: :class:`DimsHW` The stride of the convolution. Default: (1, 1) + :ivar padding: :class:`DimsHW` The padding of the convolution. The input will be zero-padded by this number of elements in the height and width directions. If the padding is asymmetric, this value corresponds to the pre-padding. Default: (0, 0) + :ivar pre_padding: :class:`DimsHW` The pre-padding. The start of input will be zero-padded by this number of elements in the height and width directions. Default: (0, 0) + :ivar post_padding: :class:`DimsHW` The post-padding. The end of input will be zero-padded by this number of elements in the height and width directions. Default: (0, 0) + :ivar padding_mode: :class:`PaddingMode` The padding mode. Padding mode takes precedence if both :attr:`IConvolutionLayer.padding_mode` and either :attr:`IConvolutionLayer.pre_padding` or :attr:`IConvolutionLayer.post_padding` are set. + :ivar num_groups: :class:`int` The number of groups for a convolution. The input tensor channels are divided into this many groups, and a convolution is executed for each group, using a filter per group. The results of the group convolutions are concatenated to form the output. **Note** When using groups in int8 mode, the size of the groups (i.e. the channel count divided by the group count) must be a multiple of 4 for both input and output. Default: 1. + :ivar kernel: :class:`Weights` The kernel weights for the convolution. The weights are specified as a contiguous array in `GKCRS` order, where `G` is the number of groups, `K` the number of output feature maps, `C` the number of input channels, and `R` and `S` are the height and width of the filter. + :ivar bias: :class:`Weights` The bias weights for the convolution. Bias is optional. To omit bias, set this to an empty :class:`Weights` object. The bias is applied per-channel, so the number of weights (if non-zero) must be equal to the number of output feature maps. + :ivar dilation: :class:`DimsHW` The dilation for a convolution. Default: (1, 1) + :ivar kernel_size_nd: :class:`Dims` The multi-dimension kernel size of the convolution. + :ivar stride_nd: :class:`Dims` The multi-dimension stride of the convolution. Default: (1, ..., 1) + :ivar padding_nd: :class:`Dims` The multi-dimension padding of the convolution. The input will be zero-padded by this number of elements in each dimension. If the padding is asymmetric, this value corresponds to the pre-padding. Default: (0, ..., 0) + :ivar dilation_nd: :class:`Dims` The multi-dimension dilation for the convolution. Default: (1, ..., 1) +)trtdoc"; +} // namespace IConvolutionLayerDoc + +namespace IFullyConnectedLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A fully connected layer in an :class:`INetworkDefinition` . + + This layer expects an input tensor of three or more non-batch dimensions. The input is automatically reshaped into an `MxV` tensor `X`, where `V` is a product of the last three dimensions and `M` is a product of the remaining dimensions (where the product over 0 dimensions is defined as 1). For example: + + - If the input tensor has shape `{C, H, W}`, then the tensor is reshaped into `{1, C*H*W}` . + - If the input tensor has shape `{P, C, H, W}`, then the tensor is reshaped into `{P, C*H*W}` . + + The layer then performs: + + :math:`Y := matmul(X, W^T) + bias` + + Where `X` is the `MxV` tensor defined above, `W` is the `KxV` weight tensor of the layer, and `bias` is a row vector size `K` that is broadcasted to `MxK` . `K` is the number of output channels, and configurable via :attr:`IFullyConnectedLayer.num_output_channels` . If `bias` is not specified, it is implicitly `0` . + + The `MxK` result `Y` is then reshaped such that the last three dimensions are `{K, 1, 1}` and the remaining dimensions match the dimensions of the input tensor. For example: + + - If the input tensor has shape `{C, H, W}`, then the output tensor will have shape `{K, 1, 1}` . + - If the input tensor has shape `{P, C, H, W}`, then the output tensor will have shape `{P, K, 1, 1}` . + + :ivar num_output_channels: :class:`int` The number of output channels `K` from the fully connected layer. + :ivar kernel: :class:`Weights` The kernel weights, given as a `KxC` matrix in row-major order. + :ivar bias: :class:`Weights` The bias weights. Bias is optional. To omit bias, set this to an empty :class:`Weights` object. +)trtdoc"; +} // namespace IFullyConnectedLayerDoc + +namespace ActivationTypeDoc +{ +constexpr const char* descr = R"trtdoc(The type of activation to perform.)trtdoc"; + +constexpr const char* RELU = R"trtdoc(Rectified Linear activation)trtdoc"; +constexpr const char* SIGMOID = R"trtdoc(Sigmoid activation)trtdoc"; +constexpr const char* TANH = R"trtdoc(Hyperbolic Tangent activation)trtdoc"; +constexpr const char* LEAKY_RELU + = R"trtdoc(Leaky Relu activation: f(x) = x if x >= 0, f(x) = alpha * x if x < 0)trtdoc"; +constexpr const char* ELU = R"trtdoc(Elu activation: f(x) = x if x >= 0, f(x) = alpha * (exp(x) - 1) if x < 0)trtdoc"; +constexpr const char* SELU + = R"trtdoc(Selu activation: f(x) = beta * x if x > 0, f(x) = beta * (alpha * exp(x) - alpha) if x <= 0)trtdoc"; +constexpr const char* SOFTSIGN = R"trtdoc(Softsign activation: f(x) = x / (1 + abs(x)))trtdoc"; +constexpr const char* SOFTPLUS = R"trtdoc(Softplus activation: f(x) = alpha * log(exp(beta * x) + 1))trtdoc"; +constexpr const char* CLIP = R"trtdoc(Clip activation: f(x) = max(alpha, min(beta, x)))trtdoc"; +constexpr const char* HARD_SIGMOID = R"trtdoc(Hard sigmoid activation: f(x) = max(0, min(1, alpha * x + beta)))trtdoc"; +constexpr const char* SCALED_TANH = R"trtdoc(Scaled Tanh activation: f(x) = alpha * tanh(beta * x))trtdoc"; +constexpr const char* THRESHOLDED_RELU + = R"trtdoc(Thresholded Relu activation: f(x) = x if x > alpha, f(x) = 0 if x <= alpha)trtdoc"; + +} // namespace ActivationTypeDoc + +namespace IActivationLayerDoc +{ +constexpr const char* descr = R"trtdoc( + An Activation layer in an :class:`INetworkDefinition` . This layer applies a per-element activation function to its input. The output has the same shape as the input. + + :ivar type: :class:`ActivationType` The type of activation to be performed. + :ivar alpha: :class:`float` The alpha parameter that is used by some parametric activations (LEAKY_RELU, ELU, SELU, SOFTPLUS, CLIP, HARD_SIGMOID, SCALED_TANH). Other activations ignore this parameter. + :ivar beta: :class:`float` The beta parameter that is used by some parametric activations (SELU, SOFTPLUS, CLIP, HARD_SIGMOID, SCALED_TANH). Other activations ignore this parameter. +)trtdoc"; +} // namespace IActivationLayerDoc + +namespace PoolingTypeDoc +{ +constexpr const char* descr = R"trtdoc(The type of pooling to perform in a pooling layer.)trtdoc"; + +constexpr const char* MAX = R"trtdoc(Maximum over elements)trtdoc"; +constexpr const char* AVERAGE + = R"trtdoc(Average over elements. If the tensor is padded, the count includes the padding)trtdoc"; +constexpr const char* MAX_AVERAGE_BLEND + = R"trtdoc(Blending between the max pooling and average pooling: `(1-blendFactor)*maxPool + blendFactor*avgPool`)trtdoc"; +} // namespace PoolingTypeDoc + +namespace IPoolingLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A Pooling layer in an :class:`INetworkDefinition` . The layer applies a reduction operation within a window over the input. + + :ivar type: :class:`PoolingType` The type of pooling to be performed. + :ivar window_size: :class:`DimsHW` The window size for pooling. + :ivar stride: :class:`DimsHW` The stride for pooling. Default: (1, 1) + :ivar padding: :class:`DimsHW` The padding for pooling. Default: (0, 0) + :ivar pre_padding: :class:`DimsHW` The pre-padding. The start of input will be zero-padded by this number of elements in the height and width directions. Default: (0, 0) + :ivar post_padding: :class:`DimsHW` The post-padding. The end of input will be zero-padded by this number of elements in the height and width directions. Default: (0, 0) + :ivar padding_mode: :class:`PaddingMode` The padding mode. Padding mode takes precedence if both :attr:`IPoolingLayer.padding_mode` and either :attr:`IPoolingLayer.pre_padding` or :attr:`IPoolingLayer.post_padding` are set. + :ivar blend_factor: :class:`float` The blending factor for the max_average_blend mode: :math:`max_average_blendPool = (1-blendFactor)*maxPool + blendFactor*avgPool` . ``blend_factor`` is a user value in [0,1] with the default value of 0.0. This value only applies for the :const:`PoolingType.MAX_AVERAGE_BLEND` mode. + :ivar average_count_excludes_padding: :class:`bool` Whether average pooling uses as a denominator the overlap area between the window and the unpadded input. If this is not set, the denominator is the overlap between the pooling window and the padded input. Default: True + :ivar window_size_nd: :class:`Dims` The multi-dimension window size for pooling. + :ivar stride_nd: :class:`Dims` The multi-dimension stride for pooling. Default: (1, ..., 1) + :ivar padding_nd: :class:`Dims` The multi-dimension padding for pooling. Default: (0, ..., 0) +)trtdoc"; +} // namespace IPoolingLayerDoc + +namespace ILRNLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A LRN layer in an :class:`INetworkDefinition` . The output size is the same as the input size. + + :ivar window_size: :class:`int` The LRN window size. The window size must be odd and in the range of [1, 15]. + :ivar alpha: :class:`float` The LRN alpha value. The valid range is [-1e20, 1e20]. + :ivar beta: :class:`float` The LRN beta value. The valid range is [0.01, 1e5f]. + :ivar k: :class:`float` The LRN K value. The valid range is [1e-5, 1e10]. +)trtdoc"; +} // namespace ILRNLayerDoc + +namespace ScaleModeDoc +{ +constexpr const char* descr = R"trtdoc(Controls how scale is applied in a Scale layer.)trtdoc"; + +constexpr const char* UNIFORM = R"trtdoc(Identical coefficients across all elements of the tensor.)trtdoc"; +constexpr const char* CHANNEL + = R"trtdoc(Per-channel coefficients. The channel dimension is assumed to be the third to last dimension.)trtdoc"; +constexpr const char* ELEMENTWISE = R"trtdoc(Elementwise coefficients.)trtdoc"; +} // namespace ScaleModeDoc + +namespace IScaleLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A Scale layer in an :class:`INetworkDefinition` . + + This layer applies a per-element computation to its input: + + :math:`output = (input * scale + shift) ^ power` + + The coefficients can be applied on a per-tensor, per-channel, or per-element basis. + + **Note** + If the number of weights is 0, then a default value is used for shift, power, and scale. The default shift is 0, the default power is 1, and the default scale is 1. + + The output size is the same as the input size. + + **Note** + The input tensor for this layer is required to have a minimum of 3 dimensions. + + :ivar mode: :class:`ScaleMode` The scale mode. + :ivar shift: :class:`Weights` The shift value. + :ivar scale: :class:`Weights` The scale value. + :ivar power: :class:`Weights` The power value. + :ivar channel_axis: :class:`int` The channel axis. +)trtdoc"; +} // namespace IScaleLayerDoc + +namespace ISoftMaxLayerDoc +{ +// TODO: Figure out how to do preformatted text inside :ivar:s +constexpr const char* descr = R"trtdoc( + A Softmax layer in an :class:`INetworkDefinition` . + + This layer applies a per-channel softmax to its input. + + The output size is the same as the input size. + + :ivar axes: :class:`int` The axes along which softmax is computed. Currently, only one axis can be set. The axis is specified by setting the bit corresponding to the axis, after excluding the batch dimension, to 1. Let's say we have an NCHW tensor as input (three non-batch dimensions). Bit 0 corresponds to the C dimension boolean. Bit 1 corresponds to the H dimension boolean. Bit 2 corresponds to the W dimension boolean. For example, to perform softmax on axis R of a NPQRCHW input, set bit 2. By default, softmax is performed on the axis which is the number of non-batch axes minus three. It is 0 if there are fewer than 3 non-batch axes. For example, if the input is NCHW, the default axis is C. If the input is NHW, then the default axis is H. +)trtdoc"; +} // namespace ISoftMaxLayerDoc + +namespace IConcatenationLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A concatenation layer in an :class:`INetworkDefinition` . + + The output channel size is the sum of the channel sizes of the inputs. + The other output sizes are the same as the other input sizes, which must all match. + + :ivar axis: :class:`int` The axis along which concatenation occurs. 0 is the major axis (excluding the batch dimension). The default is the number of non-batch axes in the tensor minus three (e.g. for an NCHW input it would be 0), or 0 if there are fewer than 3 non-batch axes. +)trtdoc"; +} // namespace IConcatenationLayerDoc + +namespace IDeconvolutionLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A deconvolution layer in an :class:`INetworkDefinition` . + + :ivar kernel_size: :class:`DimsHW` The HW kernel size of the convolution. + :ivar num_output_maps: :class:`int` The number of output feature maps for the deconvolution. + :ivar stride: :class:`DimsHW` The stride of the deconvolution. Default: (1, 1) + :ivar padding: :class:`DimsHW` The padding of the deconvolution. The input will be zero-padded by this number of elements in the height and width directions. Padding is symmetric. Default: (0, 0) + :ivar pre_padding: :class:`DimsHW` The pre-padding. The start of input will be zero-padded by this number of elements in the height and width directions. Default: (0, 0) + :ivar post_padding: :class:`DimsHW` The post-padding. The end of input will be zero-padded by this number of elements in the height and width directions. Default: (0, 0) + :ivar padding_mode: :class:`PaddingMode` The padding mode. Padding mode takes precedence if both :attr:`IDeconvolutionLayer.padding_mode` and either :attr:`IDeconvolutionLayer.pre_padding` or :attr:`IDeconvolutionLayer.post_padding` are set. + :ivar num_groups: :class:`int` The number of groups for a deconvolution. The input tensor channels are divided into this many groups, and a deconvolution is executed for each group, using a filter per group. The results of the group convolutions are concatenated to form the output. **Note** When using groups in int8 mode, the size of the groups (i.e. the channel count divided by the group count) must be a multiple of 4 for both input and output. Default: 1 + :ivar kernel: :class:`Weights` The kernel weights for the deconvolution. The weights are specified as a contiguous array in `CKRS` order, where `C` the number of input channels, `K` the number of output feature maps, and `R` and `S` are the height and width of the filter. + :ivar bias: :class:`Weights` The bias weights for the deconvolution. Bias is optional. To omit bias, set this to an empty :class:`Weights` object. The bias is applied per-feature-map, so the number of weights (if non-zero) must be equal to the number of output feature maps. + :ivar kernel_size_nd: :class:`Dims` The multi-dimension kernel size of the convolution. + :ivar stride_nd: :class:`Dims` The multi-dimension stride of the deconvolution. Default: (1, ..., 1) + :ivar padding_nd: :class:`Dims` The multi-dimension padding of the deconvolution. The input will be zero-padded by this number of elements in each dimension. Padding is symmetric. Default: (0, ..., 0) +)trtdoc"; +} // namespace IDeconvolutionLayerDoc + +namespace ElementWiseOperationDoc +{ +constexpr const char* descr = R"trtdoc(The binary operations that may be performed by an ElementWise layer.)trtdoc"; + +constexpr const char* SUM = R"trtdoc(Sum of the two elements)trtdoc"; +constexpr const char* PROD = R"trtdoc(Product of the two elements)trtdoc"; +constexpr const char* MAX = R"trtdoc(Max of the two elements)trtdoc"; +constexpr const char* MIN = R"trtdoc(Min of the two elements)trtdoc"; +constexpr const char* SUB = R"trtdoc(Subtract the second element from the first)trtdoc"; +constexpr const char* DIV = R"trtdoc(Divide the first element by the second)trtdoc"; +constexpr const char* POW = R"trtdoc(The first element to the power of the second element)trtdoc"; +constexpr const char* FLOOR_DIV = R"trtdoc(Floor division of the first element by the second)trtdoc"; +constexpr const char* AND = R"trtdoc(Logical AND of two elements)trtdoc"; +constexpr const char* OR = R"trtdoc(Logical OR of two elements)trtdoc"; +constexpr const char* XOR = R"trtdoc(Logical XOR of two elements)trtdoc"; +constexpr const char* EQUAL = R"trtdoc(Check if two elements are equal)trtdoc"; +constexpr const char* GREATER + = R"trtdoc(Check if element in first tensor is greater than corresponding element in second tensor)trtdoc"; +constexpr const char* LESS + = R"trtdoc(Check if element in first tensor is less than corresponding element in second tensor)trtdoc"; +} // namespace ElementWiseOperationDoc + +namespace IElementWiseLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A elementwise layer in an :class:`INetworkDefinition` . + + This layer applies a per-element binary operation between corresponding elements of two tensors. + + The input dimensions of the two input tensors must be equal, and the output tensor is the same size as each input. + + :ivar op: :class:`ElementWiseOperation` The binary operation for the layer. +)trtdoc"; +} // namespace IElementWiseLayerDoc + +namespace IGatherLayerDoc +{ +// TODO: Add better description here. +constexpr const char* descr = R"trtdoc( + A gather layer in an :class:`INetworkDefinition` . + + :ivar axis: :class:`int` The non-batch dimension axis to gather on. The axis must be less than the number of non-batch dimensions in the data input. + :ivar num_elementwise_dims: :class:`int` The number of leading dimensions of indices tensor to be handled elementwise. Must be 0 if there is an implicit batch dimension. It can be 0 or 1 if there is not an implicit batch dimension. +)trtdoc"; +} // namespace IGatherLayerDoc + +namespace RNNOperationDoc +{ +constexpr const char* descr = R"trtdoc( + The RNN operations that may be performed by an RNN layer. + + **Equation definitions** + + In the equations below, we use the following naming convention: + + | `t` := current time step + | `i` := input gate + | `o` := output gate + | `f` := forget gate + | `z` := update gate + | `r` := reset gate + | `c` := cell gate + | `h` := hidden gate + + | `g[t]` denotes the output of gate g at timestep `t`, e.g.`f[t]` is the output of the forget gate `f` . + | `X[t]` := input tensor for timestep `t` + | `C[t]` := cell state for timestep `t` + | `H[t]` := hidden state for timestep `t` + + | `W[g]` := `W` (input) parameter weight matrix for gate `g` + | `R[g]` := `U` (recurrent) parameter weight matrix for gate `g` + | `Wb[g]` := `W` (input) parameter bias vector for gate `g` + | `Rb[g]` := `U` (recurrent) parameter bias vector for gate `g` + + Unless otherwise specified, all operations apply pointwise to elements of each operand tensor. + + | `ReLU(X)` := `max(X, 0)` + | `tanh(X)` := hyperbolic tangent of `X` + | `sigmoid(X)` := `1 / (1 + exp(-X))` + | `exp(X)` := `e^X` + | `A.B` denotes matrix multiplication of `A` and `B` . + | `A*B` denotes pointwise multiplication of `A` and `B` . + + **Equations** + + Depending on the value of RNNOperation chosen, each sub-layer of the RNN layer will perform one of the following operations: + + **RELU** + + :math:`H[t] := ReLU(W[i].X[t] + R[i].H[t-1] + Wb[i] + Rb[i])` + + **TANH** + + :math:`H[t] := tanh(W[i].X[t] + R[i].H[t-1] + Wb[i] + Rb[i])` + + **LSTM** + + | :math:`i[t] := sigmoid(W[i].X[t] + R[i].H[t-1] + Wb[i] + Rb[i])` + | :math:`f[t] := sigmoid(W[f].X[t] + R[f].H[t-1] + Wb[f] + Rb[f])` + | :math:`o[t] := sigmoid(W[o].X[t] + R[o].H[t-1] + Wb[o] + Rb[o])` + | :math:`c[t] := tanh(W[c].X[t] + R[c].H[t-1] + Wb[c] + Rb[c])` + + + | :math:`C[t] := f[t]*C[t-1] + i[t]*c[t]` + | :math:`H[t] := o[t]*tanh(C[t])` + + **GRU** + + | :math:`z[t] := sigmoid(W[z].X[t] + R[z].H[t-1] + Wb[z] + Rb[z])` + | :math:`r[t] := sigmoid(W[r].X[t] + R[r].H[t-1] + Wb[r] + Rb[r])` + | :math:`h[t] := tanh(W[h].X[t] + r[t]*(R[h].H[t-1] + Rb[h]) + Wb[h])` + | :math:`H[t] := (1 - z[t])*h[t] + z[t]*H[t-1]` +)trtdoc"; + +constexpr const char* RELU = R"trtdoc(Single gate RNN w/ ReLU activation)trtdoc"; +constexpr const char* TANH = R"trtdoc(Single gate RNN w/ TANH activation)trtdoc"; +constexpr const char* LSTM = R"trtdoc(Four-gate LSTM network w/o peephole connections)trtdoc"; +constexpr const char* GRU = R"trtdoc(Three-gate network consisting of Gated Recurrent Units)trtdoc"; + +} // namespace RNNOperationDoc + +namespace RNNDirectionDoc +{ +constexpr const char* descr = R"trtdoc(The RNN direction that may be performed by an RNN layer.)trtdoc"; + +constexpr const char* UNIDIRECTION = R"trtdoc(Network iterates from first input to last input)trtdoc"; +constexpr const char* BIDIRECTION + = R"trtdoc(Network iterates from first to last (and vice versa) and outputs concatenated)trtdoc"; +} // namespace RNNDirectionDoc + +namespace RNNInputModeDoc +{ +constexpr const char* descr = R"trtdoc( + The RNN input modes that may occur with an RNN layer. + + If the RNN is configured with :const:`RNNInputMode.LINEAR` , then for each gate `g` in the first layer of the RNN, + the input vector `X[t]` (length `E`) is left-multiplied by the gate's corresponding weight matrix `W[g]` + (dimensions `HxE`) as usual, before being used to compute the gate output as described by :class:`RNNOperation` . + + If the RNN is configured with :const:`RNNInputMode.SKIP` , then this initial matrix multiplication is "skipped" + and `W[g]` is conceptually an identity matrix. In this case, the input vector `X[t]` must have length `H` + (the size of the hidden state). +)trtdoc"; + +constexpr const char* LINEAR = R"trtdoc(Perform the normal matrix multiplication in the first recurrent layer)trtdoc"; +constexpr const char* SKIP = R"trtdoc(No operation is performed on the first recurrent layer)trtdoc"; +} // namespace RNNInputModeDoc + +namespace RNNGateTypeDoc +{ +constexpr const char* descr = R"trtdoc( + The RNN input modes that may occur with an RNN layer. + + If the RNN is configured with :const:`RNNInputMode.LINEAR` , then for each gate `g` in the first layer of the RNN, + the input vector `X[t]` (length `E`) is left-multiplied by the gate's corresponding weight matrix `W[g]` + (dimensions `HxE`) as usual, before being used to compute the gate output as described by :class:`RNNOperation` . + + If the RNN is configured with :const:`RNNInputMode.SKIP` , then this initial matrix multiplication is "skipped" + and `W[g]` is conceptually an identity matrix. In this case, the input vector `X[t]` must have length `H` + (the size of the hidden state). +)trtdoc"; + +constexpr const char* INPUT = R"trtdoc(Input Gate)trtdoc"; +constexpr const char* OUTPUT = R"trtdoc(Output Gate)trtdoc"; +constexpr const char* FORGET = R"trtdoc(Forget Gate)trtdoc"; +constexpr const char* UPDATE = R"trtdoc(Update Gate)trtdoc"; +constexpr const char* RESET = R"trtdoc(Reset Gate)trtdoc"; +constexpr const char* CELL = R"trtdoc(Cell Gate)trtdoc"; +constexpr const char* HIDDEN = R"trtdoc(Hidden Gate)trtdoc"; +} // namespace RNNGateTypeDoc + +namespace IRNNv2LayerDoc +{ +constexpr const char* descr = R"trtdoc( + An RNN layer in an :class:`INetworkDefinition` , version 2 + + :ivar num_layers: :class:`int` The layer count of the RNN. + :ivar hidden_size: :class:`int` The hidden size of the RNN. + :ivar max_seq_length: :class:`int` The maximum sequence length of the RNN + :ivar data_length: :class:`int` The length of the data being processed by the RNN for use in computing other values. + + :ivar seq_lengths: :class:`ITensor` Individual sequence lengths in the batch with the :class:`ITensor` provided. + The :attr:`seq_lengths` :class:`ITensor` should be a {N1, ..., Np} tensor, where N1..Np are the index dimensions + of the input tensor to the RNN. + If :attr:`seq_lengths` is not specified, then the RNN layer assumes all sequences are size :attr:`max_seq_length` . + All sequence lengths in :attr:`seq_lengths` should be in the range [1, :attr:`max_seq_length` ]. Zero-length sequences are not supported. + This tensor must be of type int32. + :ivar op: :class:`RNNOperation` The operation of the RNN layer. + :ivar input_mode: :class:`int` The input mode of the RNN layer. + :ivar direction: :class:`int` The direction of the RNN layer. + + :ivar hidden_state: :class:`ITensor` the initial hidden state of the RNN with the provided :attr:`hidden_state` :class:`ITensor` . + The :attr:`hidden_state` :class:`ITensor` should have the dimensions `{N1, ..., Np, L, H}`, where: + `N1..Np` are the index dimensions specified by the input tensor + `L` is the number of layers in the RNN, equal to :attr:`num_layers` + `H` is the hidden state for each layer, equal to :attr:`hidden_size` if :attr:`direction` is :const:`RNNDirection.UNIDIRECTION` , and 2x :attr:`hidden_size` otherwise. + :ivar cell_state: :class:`ITensor` The initial cell state of the LSTM with the provided :attr:`cell_state` :class:`ITensor` . + The :attr:`cell_state` :class:`ITensor` should have the dimensions `{N1, ..., Np, L, H}`, where: + `N1..Np` are the index dimensions specified by the input tensor + `L` is the number of layers in the RNN, equal to :attr:`num_layers` + `H` is the hidden state for each layer, equal to :attr:`hidden_size` if :attr:`direction` is :const:`RNNDirection.UNIDIRECTION`, and 2x :attr:`hidden_size` otherwise. + It is an error to set this on an RNN layer that is not configured with :const:`RNNOperation.LSTM` . +)trtdoc"; + +constexpr const char* set_weights_for_gate = R"trtdoc( + Set the weight parameters for an individual gate in the RNN. + + :arg layer_index: The index of the layer that contains this gate. + :arg gate: The name of the gate within the RNN layer. The gate name must correspond to one of the gates used by this layer's :class:`RNNOperation` . + :arg is_w: True if the weight parameters are for the input matrix W[g] and false if they are for the recurrent input matrix R[g]. See :class:`RNNOperation` for equations showing how these matrices are used in the RNN gate. + :arg weights: The weight structure holding the weight parameters, which are stored as a row-major 2D matrix. For more information, see `IRNNv2Layer::setWeights() `_. +)trtdoc"; +constexpr const char* get_weights_for_gate = R"trtdoc( + Get the weight parameters for an individual gate in the RNN. + + :arg layer_index: The index of the layer that contains this gate. + :arg gate: The name of the gate within the RNN layer. + :arg is_w: True if the weight parameters are for the input matrix W[g] and false if they are for the recurrent input matrix R[g]. + + :returns: The weight parameters. +)trtdoc"; +constexpr const char* set_bias_for_gate = R"trtdoc( + Set the bias parameters for an individual gate in the RNN. + + :arg layer_index: The index of the layer that contains this gate. + :arg gate: The name of the gate within the RNN layer. The gate name must correspond to one of the gates used by this layer's :class:`RNNOperation` . + :arg is_w: True if the bias parameters are for the input bias Wb[g] and false if they are for the recurrent input bias Rb[g]. See + :class:`RNNOperation` for equations showing how these bias vectors are used in the RNN gate. + :arg bias: The weight structure holding the bias parameters, which should be an array of size :attr:`hidden_size` . +)trtdoc"; +constexpr const char* get_bias_for_gate = R"trtdoc( + Get the bias parameters for an individual gate in the RNN. + + :arg layer_index: The index of the layer that contains this gate. + :arg gate: The name of the gate within the RNN layer. + :arg is_w: True if the bias parameters are for the input bias Wb[g] and false if they are for the recurrent input bias Rb[g]. + + :returns: The bias parameters. +)trtdoc"; +} // namespace IRNNv2LayerDoc + +namespace IPluginV2LayerDoc +{ +constexpr const char* descr = R"trtdoc( + A plugin layer in an :class:`INetworkDefinition` . + + :ivar plugin: :class:`IPluginV2` The plugin for the layer. +)trtdoc"; +} // namespace IPluginV2LayerDoc + +namespace UnaryOperationDoc +{ +constexpr const char* descr = R"trtdoc(The unary operations that may be performed by a Unary layer.)trtdoc"; + +constexpr const char* EXP = R"trtdoc(Exponentiation)trtdoc"; +constexpr const char* LOG = R"trtdoc(Log (base e))trtdoc"; +constexpr const char* SQRT = R"trtdoc(Square root)trtdoc"; +constexpr const char* RECIP = R"trtdoc(Reciprocal)trtdoc"; +constexpr const char* ABS = R"trtdoc(Absolute value)trtdoc"; +constexpr const char* NEG = R"trtdoc(Negation)trtdoc"; +constexpr const char* SIN = R"trtdoc(Sine)trtdoc"; +constexpr const char* COS = R"trtdoc(Cosine)trtdoc"; +constexpr const char* TAN = R"trtdoc(Tangent)trtdoc"; +constexpr const char* SINH = R"trtdoc(Hyperbolic sine)trtdoc"; +constexpr const char* COSH = R"trtdoc(Hyperbolic cosine)trtdoc"; +constexpr const char* ASIN = R"trtdoc(Inverse sine)trtdoc"; +constexpr const char* ACOS = R"trtdoc(Inverse cosine)trtdoc"; +constexpr const char* ATAN = R"trtdoc(Inverse tangent)trtdoc"; +constexpr const char* ASINH = R"trtdoc(Inverse hyperbolic sine)trtdoc"; +constexpr const char* ACOSH = R"trtdoc(Inverse hyperbolic cosine)trtdoc"; +constexpr const char* ATANH = R"trtdoc(Inverse hyperbolic tangent)trtdoc"; +constexpr const char* CEIL = R"trtdoc(Ceiling)trtdoc"; +constexpr const char* FLOOR = R"trtdoc(Floor)trtdoc"; +constexpr const char* ERF = R"trtdoc(Gauss error function)trtdoc"; +constexpr const char* NOT = R"trtdoc(Not)trtdoc"; +} // namespace UnaryOperationDoc + +namespace IUnaryLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A unary layer in an :class:`INetworkDefinition` . + + :ivar op: :class:`UnaryOperation` The unary operation for the layer. +)trtdoc"; +} // namespace IUnaryLayerDoc + +namespace ReduceOperationDoc +{ +constexpr const char* descr = R"trtdoc(The reduce operations that may be performed by a Reduce layer)trtdoc"; + +constexpr const char* SUM = R"trtdoc()trtdoc"; +constexpr const char* PROD = R"trtdoc()trtdoc"; +constexpr const char* MAX = R"trtdoc()trtdoc"; +constexpr const char* MIN = R"trtdoc()trtdoc"; +constexpr const char* AVG = R"trtdoc()trtdoc"; +} // namespace ReduceOperationDoc + +namespace IReduceLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A reduce layer in an :class:`INetworkDefinition` . + + :ivar op: :class:`ReduceOperation` The reduce operation for the layer. + :ivar axes: :class:`int` The axes over which to reduce. + :ivar keep_dims: :class:`bool` Specifies whether or not to keep the reduced dimensions for the layer. +)trtdoc"; +} // namespace IReduceLayerDoc + +namespace IPaddingLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A padding layer in an :class:`INetworkDefinition` . + + :ivar pre_padding: :class:`DimsHW` The padding that is applied at the start of the tensor. Negative padding results in trimming the edge by the specified amount. + :ivar post_padding: :class:`DimsHW` The padding that is applied at the end of the tensor. Negative padding results in trimming the edge by the specified amount + :ivar pre_padding_nd: :class:`Dims` The padding that is applied at the start of the tensor. Negative padding results in trimming the edge by the specified amount. Only 2 dimensions currently supported. + :ivar post_padding_nd: :class:`Dims` The padding that is applied at the end of the tensor. Negative padding results in trimming the edge by the specified amount. Only 2 dimensions currently supported. +)trtdoc"; +} // namespace IPaddingLayerDoc + +namespace PermutationDoc +{ +constexpr const char* descr = R"trtdoc( + The elements of the permutation. The permutation is applied as outputDimensionIndex = permutation[inputDimensionIndex], so to permute from CHW order to HWC order, the required permutation is [1, 2, 0], and to permute from HWC to CHW, the required permutation is [2, 0, 1]. + + It supports iteration and indexing and is implicitly convertible to/from Python iterables (like :class:`tuple` or :class:`list` ). Therefore, you can use those classes in place of :class:`Permutation` . +)trtdoc"; +} // namespace PermutationDoc + +namespace IShuffleLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A shuffle layer in an :class:`INetworkDefinition` . + + This class shuffles data by applying in sequence: a transpose operation, a reshape operation and a second transpose operation. The dimension types of the output are those of the reshape dimension. + + :ivar first_transpose: :class:`Permutation` The permutation applied by the first transpose operation. Default: Identity Permutation + :ivar reshape_dims: :class:`Dims` The reshaped dimensions. + Two special values can be used as dimensions. + Value 0 copies the corresponding dimension from input. This special value can be used more than once in the dimensions. If number of reshape dimensions is less than input, 0s are resolved by aligning the most significant dimensions of input. + Value -1 infers that particular dimension by looking at input and rest of the reshape dimensions. Note that only a maximum of one dimension is permitted to be specified as -1. + The product of the new dimensions must be equal to the product of the old. + :ivar second_transpose: :class:`Permutation` The permutation applied by the second transpose operation. Default: Identity Permutation + :ivar zero_is_placeholder: :class:`bool` The meaning of 0 in reshape dimensions. + If true, then a 0 in the reshape dimensions denotes copying the corresponding + dimension from the first input tensor. If false, then a 0 in the reshape + dimensions denotes a zero-length dimension. +)trtdoc"; + +constexpr const char* set_input = R"trtdoc( + Sets the input tensor for the given index. The index must be 0 for a static shuffle layer. + A static shuffle layer is converted to a dynamic shuffle layer by calling :func:`set_input` with an index 1. + A dynamic shuffle layer cannot be converted back to a static shuffle layer. + + For a dynamic shuffle layer, the values 0 and 1 are valid. + The indices in the dynamic case are as follows: + + ======= ======================================================================== + Index Description + ======= ======================================================================== + 0 Data or Shape tensor to be shuffled. + 1 The dimensions for the reshape operation, as a 1D Int32 shape tensor. + ======= ======================================================================== + + If this function is called with a value 1, then :attr:`num_inputs` changes + from 1 to 2. + + :arg index: The index of the input tensor. + :arg tensor: The input tensor. +)trtdoc"; + +} // namespace IShuffleLayerDoc + +namespace ISliceLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A slice layer in an :class:`INetworkDefinition` . + + :ivar start: :class:`Dims` The start offset. + :ivar shape: :class:`Dims` The output dimensions. + :ivar stride: :class:`Dims` The slicing stride. + :ivar mode: :class:`SliceMode` Controls how ISliceLayer handles out of bounds coordinates. +)trtdoc"; + +constexpr const char* set_input = R"trtdoc( + Sets the input tensor for the given index. The index must be 0 for a static slice layer. + A static slice layer is converted to a dynamic slice layer by calling :func:`set_input` with an index > 0. + A dynamic slice layer cannot be converted back to a static slice layer. + + For a dynamic slice layer, the values 0-3 are valid. If an index > 0 is specified, all values between + index 0 and that index must be dynamic tensors. The values larger than index can use static dimensions. + For example, if an index of two is specified, the stride tensor can be set via setStride, but the start tensor + must be specified via :func:`set_input` as both size and start are converted to dynamic tensors. + The indices in the dynamic case are as follows: + + ===== ================================================================================== + Index Description + ===== ================================================================================== + 0 Data or Shape tensor to be sliced. + 1 The start tensor to begin slicing, N-dimensional for Data, and 1-D for Shape. + 2 The size tensor of the resulting slice, N-dimensional for Data, and 1-D for Shape. + 3 The stride of the slicing operation, N-dimensional for Data, and 1-D for Shape. + ===== ================================================================================== + + If this function is called with a value greater than 0, then :attr:`num_inputs` changes + from 1 to index + 1. When converting from static to dynamic slice layer, + all unset tensors, between 1 and index + 1, are initialized to nullptr. It is an error to attempt to build + a network that has any nullptr inputs. + + :arg index: The index of the input tensor. + :arg tensor: The input tensor. +)trtdoc"; + +} // namespace ISliceLayerDoc + +namespace SliceModeDoc +{ +constexpr const char* descr = R"trtdoc(Controls how ISliceLayer handles out of bounds coordinates)trtdoc"; + +constexpr const char* DEFAULT + = R"trtdoc(Fail with error when the coordinates are out of bounds. This is the default.)trtdoc"; +constexpr const char* WRAP = R"trtdoc(Coordinates wrap around periodically)trtdoc"; +} // namespace SliceModeDoc + +namespace IShapeLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A shape layer in an :class:`INetworkDefinition` . Used for getting the shape of a tensor. + This class sets the output to a one-dimensional tensor with the dimensions of the input tensor. + + For example, if the input is a four-dimensional tensor (of any type) with + dimensions [2,3,5,7], the output tensor is a one-dimensional Int32 tensor + of length 4 containing the sequence 2, 3, 5, 7. +)trtdoc"; + +} // namespace IShapeLayerDoc + +namespace TopKOperationDoc +{ +constexpr const char* descr = R"trtdoc(The operations that may be performed by a TopK layer)trtdoc"; + +constexpr const char* MAX = R"trtdoc(Maximum of the elements)trtdoc"; +constexpr const char* MIN = R"trtdoc(Minimum of the elements)trtdoc"; +} // namespace TopKOperationDoc + +namespace ITopKLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A TopK layer in an :class:`INetworkDefinition` . + + :ivar op: :class:`TopKOperation` The operation for the layer. + :ivar k: :class:`TopKOperation` the k value for the layer. Currently only values up to 25 are supported. + :ivar axes: :class:`TopKOperation` The axes along which to reduce. +)trtdoc"; +} // namespace ITopKLayerDoc + +namespace MatrixOperationDoc +{ +constexpr const char* descr = R"trtdoc(The matrix operations that may be performed by a Matrix layer)trtdoc"; + +constexpr const char* NONE = R"trtdoc()trtdoc"; +constexpr const char* TRANSPOSE = R"trtdoc(Transpose each matrix)trtdoc"; +constexpr const char* VECTOR = R"trtdoc(Treat operand as collection of vectors)trtdoc"; +} // namespace MatrixOperationDoc + +namespace IMatrixMultiplyLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A matrix multiply layer in an :class:`INetworkDefinition` . + + Let A be op(getInput(0)) and B be op(getInput(1)) where + op(x) denotes the corresponding MatrixOperation. + + When A and B are matrices or vectors, computes the inner product A * B: + + | matrix * matrix -> matrix + | matrix * vector -> vector + | vector * matrix -> vector + | vector * vector -> scalar + + Inputs of higher rank are treated as collections of matrices or vectors. + The output will be a corresponding collection of matrices, vectors, or scalars. + + :ivar op0: :class:`MatrixOperation` How to treat the first input. + :ivar op1: :class:`MatrixOperation` How to treat the second input. +)trtdoc"; +} // namespace IMatrixMultiplyLayerDoc + +namespace IRaggedSoftMaxLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A ragged softmax layer in an :class:`INetworkDefinition` . + + This layer takes a ZxS input tensor and an additional Zx1 bounds tensor holding the lengths of the Z sequences. + + This layer computes a softmax across each of the Z sequences. + + The output tensor is of the same size as the input tensor. +)trtdoc"; +} // namespace IRaggedSoftMaxLayerDoc + +namespace IIdentityLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A layer that represents the identity function. + + If tensor precision is explicitly specified, it can be used to transform from one precision to another. +)trtdoc"; +} // namespace IIdentityLayerDoc + +namespace IConstantLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A constant layer in an :class:`INetworkDefinition` . + + Note: This layer does not support boolean types. + + :ivar weights: :class:`Weights` The weights for the layer. + :ivar shape: :class:`Dims` The shape of the layer. +)trtdoc"; +} // namespace IConstantLayerDoc + +namespace IParametricReLULayerDoc +{ +constexpr const char* descr = R"trtdoc( + A parametric ReLU layer in an :class:`INetworkDefinition` . + + This layer applies a parametric ReLU activation to an input tensor (first input), with slopes taken from a + slopes tensor (second input). This can be viewed as a leaky ReLU operation where the negative slope differs + from element to element (and can in fact be learned). + + The slopes tensor must be unidirectional broadcastable to the input tensor: the rank of the two tensors must + be the same, and all dimensions of the slopes tensor must either equal the input tensor or be 1. + The output tensor has the same shape as the input tensor. +)trtdoc"; +} // namespace IParametricReLULayerDoc + +namespace ResizeModeDoc +{ +constexpr const char* descr = R"trtdoc(Various modes of resize in the resize layer.)trtdoc"; + +constexpr const char* NEAREST = R"trtdoc(1D, 2D, and 3D nearest neighbor resizing.)trtdoc"; +constexpr const char* LINEAR = R"trtdoc(Can handle linear, bilinear, trilinear resizing.)trtdoc"; +} // namespace ResizeModeDoc + +namespace ResizeCoordinateTransformationDoc +{ +constexpr const char* descr + = R"trtdoc(Various modes of how to map the resized coordinate back to the original coordinate.)trtdoc"; + +constexpr const char* ALIGN_CORNERS + = R"trtdoc(In this mode, map the resized coordinate back to the original coordinate by the formula: x_original = x_resized * (length_original - 1) / (length_resized - 1).)trtdoc"; +constexpr const char* ASYMMETRIC + = R"trtdoc(In this mode, map the resized coordinate back to the original coordinate by the formula: x_original = x_resized * (length_original / length_resized).)trtdoc"; +constexpr const char* HALF_PIXEL + = R"trtdoc(In this mode, map the resized coordinate back to the original coordinate by the formula: x_original = (x_resized + 0.5) * (length_original / length_resized) - 0.5.)trtdoc"; +} // namespace ResizeCoordinateTransformationDoc + +namespace ResizeSelectorDoc +{ +constexpr const char* descr + = R"trtdoc(Decides whether the original coordinate is 0 given a resize coordinate less than 2.)trtdoc"; + +constexpr const char* FORMULA = R"trtdoc(Use the transformation formula to calculate the original coordinate.)trtdoc"; +constexpr const char* UPPER + = R"trtdoc(Return the original coordinate index as 0 given a resize coordinate is less than 2.)trtdoc"; +} // namespace ResizeSelectorDoc + +namespace ResizeRoundModeDoc +{ +constexpr const char* descr = R"trtdoc(Rounding modes available for the resize layer.)trtdoc"; + +constexpr const char* HALF_UP + = R"trtdoc(Round original floating-point coordinate to the nearest integer value, with halfway cases rounded up.)trtdoc"; +constexpr const char* HALF_DOWN + = R"trtdoc(Round original floating-point coordinate to the nearest integer value, with halfway cases rounded down.)trtdoc"; +constexpr const char* FLOOR + = R"trtdoc(Round original floating-point coordinate to the nearest integer value less than it.)trtdoc"; +constexpr const char* CEIL + = R"trtdoc(Round original floating-point coordinate to the nearest integer value larger than it.)trtdoc"; +} // namespace ResizeRoundModeDoc + +namespace IResizeLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A resize layer in an :class:`INetworkDefinition` . + + Resize layer can be used for resizing a N-D tensor. + + Resize layer currently supports the following configurations: + + * ResizeMode.NEAREST - resizes innermost `m` dimensions of N-D, where 0 < m <= min(3, N) and N > 0. + * ResizeMode.LINEAR - resizes innermost `m` dimensions of N-D, where 0 < m <= min(3, N) and N > 0. + + Default resize mode is ResizeMode.NEAREST. + + Resize layer provides two ways to resize tensor dimensions: + + * Set output dimensions directly. It can be done for static as well as dynamic resize layer. + Static resize layer requires output dimensions to be known at build-time. + Dynamic resize layer requires output dimensions to be set as one of the input tensors. + + * Set scales for resize. Each output dimension is calculated as floor(input dimension * scale). + Only static resize layer allows setting scales where the scales are known at build-time. + + :ivar shape: :class:`Dims` The output dimensions. Must to equal to input dimensions size. + :ivar scales: :class:`List[float]` List of resize scales. + :ivar resize_mode: :class:`ResizeMode` Resize mode can be Linear or Nearest. + :ivar coordinate_transformation: :class:`ResizeCoordinateTransformationDoc` Supported resize coordinate transformation modes are ALIGN_CORNERS, ASYMMETRIC and HALF_PIXEL. + :ivar selector_for_single_pixel: :class:`ResizeSelector` Supported resize selector modes are FORMULA and UPPER. + :ivar nearest_rounding: :class:`ResizeRoundMode` Supported resize Round modes are HALF_UP, HALF_DOWN, FLOOR and CEIL. +)trtdoc"; + +constexpr const char* set_input = R"trtdoc( + Sets the input tensor for the given index. + + If index == 1 and num_inputs == 1, and there is no implicit batch dimension, + in which case num_inputs changes to 2. + Once such additional input is set, resize layer works in dynamic mode. + When index == 1 and num_inputs == 1, the output dimensions are used from + the input tensor, overriding the dimensions supplied by `shape`. + + :arg index: The index of the input tensor. + :arg tensor: The input tensor. +)trtdoc"; +} // namespace IResizeLayerDoc + +namespace LoopOutputDoc +{ +constexpr const char* descr = R"trtdoc(Describes kinds of loop outputs.)trtdoc"; + +constexpr const char* LAST_VALUE = R"trtdoc(Output value is value of tensor for last iteration.)trtdoc"; +constexpr const char* CONCATENATE + = R"trtdoc(Output value is concatenation of values of tensor for each iteration, in forward order.)trtdoc"; +constexpr const char* REVERSE + = R"trtdoc(Output value is concatenation of values of tensor for each iteration, in reverse order.)trtdoc"; +} // namespace LoopOutputDoc + +namespace TripLimitDoc +{ +constexpr const char* descr = R"trtdoc(Describes kinds of trip limits.)trtdoc"; + +constexpr const char* COUNT = R"trtdoc(Tensor is scalar of type kINT32 that contains the trip count.)trtdoc"; +constexpr const char* WHILE = R"trtdoc(Tensor is a scalar of type BOOL. Loop terminates when value is false.)trtdoc"; + +} // namespace TripLimitDoc + +namespace ILoopBoundaryLayerDoc +{ +constexpr const char* descr = R"trtdoc( + :ivar loop: :class:`ILoop` associated with this boundary layer. +)trtdoc"; + +} // namespace ILoopBoundaryLayerDoc + +namespace IRecurrenceLayerDoc +{ +constexpr const char* descr = R"trtdoc()trtdoc"; +constexpr const char* set_input = R"trtdoc( + Set the first or second input. + If index==1 and the number of inputs is one, the input is appended. + The first input specifies the initial output value, and must come from outside the loop. + The second input specifies the next output value, and must come from inside the loop. + The two inputs must have the same dimensions. + + :param index: The index of the input to set. + :param tensor: The input tensor. +)trtdoc"; +} // namespace IRecurrenceLayerDoc + +namespace ILoopOutputLayerDoc +{ +constexpr const char* descr = R"trtdoc( + An :class:`ILoopOutputLayer` is the sole way to get output from a loop. + + The first input tensor must be defined inside the loop; the output tensor is outside the loop. + The second input tensor, if present, must be defined outside the loop. + + If :attr:`kind` is ``LAST_VALUE``, a single input must be provided. + + If :attr:`kind` is ``CONCATENATE`` or ``REVERSE``, a second input must be provided. + The second input must be a scalar “shape tensor”, defined before the loop commences, + that specifies the concatenation length of the output. + + The output tensor has j more dimensions than the input tensor, where + j == 0 if :attr:`kind` is ``LAST_VALUE`` + j == 1 if :attr:`kind` is ``CONCATENATE`` or ``REVERSE``. + + :ivar axis: The contenation axis. Ignored if :attr:`kind` is ``LAST_VALUE``. + For example, if the input tensor has dimensions [b,c,d], + and :attr:`kind` is ``CONCATENATE``, the output has four dimensions. + Let a be the value of the second input. + axis=0 causes the output to have dimensions [a,b,c,d]. + axis=1 causes the output to have dimensions [b,a,c,d]. + axis=2 causes the output to have dimensions [b,c,a,d]. + axis=3 causes the output to have dimensions [b,c,d,a]. + Default is axis is 0. + :ivar kind: The kind of loop output. See :class:`LoopOutput` +)trtdoc"; + +constexpr const char* set_input = R"trtdoc( + Like :func:`ILayer.set_input`, but additionally works if index==1, :attr:`num_inputs`==1, in which case :attr:`num_inputs` changes to 2. +)trtdoc"; + +} // namespace ILoopOutputLayerDoc + +namespace ITripLimitLayerDoc +{ +constexpr const char* descr = R"trtdoc( + :ivar kind: The kind of trip limit. See :class:`TripLimit` +)trtdoc"; +} // namespace ITripLimitLayerDoc + +namespace IIteratorLayerDoc +{ +constexpr const char* descr = R"trtdoc( + :ivar axis: The axis to iterate over + :ivar reverse: For reverse=false, the layer is equivalent to add_gather(tensor, I, 0) where I is a + scalar tensor containing the loop iteration number. + For reverse=true, the layer is equivalent to add_gather(tensor, M-1-I, 0) where M is the trip count + computed from TripLimits of kind ``COUNT``. + The default is reverse=false. +)trtdoc"; +} // namespace IIteratorLayerDoc + +namespace ILoopDoc +{ +constexpr const char* descr = R"trtdoc( + Helper for creating a recurrent subgraph. + + :ivar name: The name of the loop. The name is used in error diagnostics. +)trtdoc"; + +constexpr const char* add_recurrence = R"trtdoc( + Create a recurrence layer for this loop with initial_value as its first input. + + :param initial_value: The initial value of the recurrence layer. - :ivar axis: :class:`int` The non-batch dimension axis to gather on. The axis must be less than the number of non-batch dimensions in the data input. - :ivar num_elementwise_dims: :class:`int` The number of leading dimensions of indices tensor to be handled elementwise. Must be 0 if there is an implicit batch dimension. It can be 0 or 1 if there is not an implicit batch dimension. - )trtdoc"; - } // IGatherLayerDoc - - namespace RNNOperationDoc - { - constexpr const char* descr = R"trtdoc( - The RNN operations that may be performed by an RNN layer. + :returns: The added :class:`IRecurrenceLayer` , or :class:`None` if it could not be created. +)trtdoc"; + +constexpr const char* add_trip_limit = R"trtdoc( + Add a trip-count limiter, based on the given tensor. + + There may be at most one ``COUNT`` and one ``WHILE`` limiter for a loop. + When both trip limits exist, the loop exits when the + count is reached or condition is falsified. + It is an error to not add at least one trip limiter. + + For ``WHILE``, the input tensor must be the output of a subgraph that contains + only layers that are not :class:`ITripLimitLayer` , :class:`IIteratorLayer` or :class:`ILoopOutputLayer` . + Any :class:`IRecurrenceLayer` s in the subgraph must belong to the same loop as the + :class:`ITripLimitLayer` . A trivial example of this rule is that the input to the ``WHILE`` + is the output of an :class:`IRecurrenceLayer` for the same loop. + + + :param tensor: The input tensor. Must be available before the loop starts. + :param kind: The kind of trip limit. See :class:`TripLimit` + + :returns: The added :class:`ITripLimitLayer` , or :class:`None` if it could not be created. +)trtdoc"; + +constexpr const char* add_iterator = R"trtdoc( + Return layer that subscripts tensor by loop iteration. + + For reverse=false, this is equivalent to add_gather(tensor, I, 0) where I is a + scalar tensor containing the loop iteration number. + For reverse=true, this is equivalent to add_gather(tensor, M-1-I, 0) where M is the trip count + computed from TripLimits of kind ``COUNT``. + + :param tensor: The tensor to iterate over. + :param axis: The axis along which to iterate. + :param reverse: Whether to iterate in the reverse direction. - **Equation definitions** + :returns: The :class:`IIteratorLayer` , or :class:`None` if it could not be created. +)trtdoc"; + +constexpr const char* add_loop_output = R"trtdoc( + Make an output for this loop, based on the given tensor. + + If ``kind`` is ``CONCATENATE`` or ``REVERSE``, a second input specifying the + concatenation dimension must be added via method :func:`ILoopOutputLayer.set_input` . + + :param kind: The kind of loop output. See :class:`LoopOutput` + :param axis: The axis for concatenation (if using ``kind`` of ``CONCATENATE`` or ``REVERSE``). + + :returns: The added :class:`ILoopOutputLayer` , or :class:`None` if it could not be created. +)trtdoc"; + +} // namespace ILoopDoc + +namespace ISelectLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A select layer in an :class:`INetworkDefinition` . + + This layer implements an element-wise ternary conditional operation. Wherever ``condition`` is ``True``, elements are taken from the first input, and wherever ``condition`` is ``False``, elements are taken from the second input. +)trtdoc"; +} // namespace ISelectLayerDoc + +namespace FillOperationDoc +{ +constexpr const char* descr = R"trtdoc(The tensor fill operations that may performed by an Fill layer.)trtdoc"; - In the equations below, we use the following naming convention: +constexpr const char* LINSPACE = R"trtdoc(Generate evenly spaced numbers over a specified interval)trtdoc"; +constexpr const char* RANDOM_UNIFORM + = R"trtdoc(Generate a tensor with random values drawn from a uniform distribution)trtdoc"; +} // namespace FillOperationDoc - | `t` := current time step - | `i` := input gate - | `o` := output gate - | `f` := forget gate - | `z` := update gate - | `r` := reset gate - | `c` := cell gate - | `h` := hidden gate - - | `g[t]` denotes the output of gate g at timestep `t`, e.g.`f[t]` is the output of the forget gate `f` . - | `X[t]` := input tensor for timestep `t` - | `C[t]` := cell state for timestep `t` - | `H[t]` := hidden state for timestep `t` - - | `W[g]` := `W` (input) parameter weight matrix for gate `g` - | `R[g]` := `U` (recurrent) parameter weight matrix for gate `g` - | `Wb[g]` := `W` (input) parameter bias vector for gate `g` - | `Rb[g]` := `U` (recurrent) parameter bias vector for gate `g` - - Unless otherwise specified, all operations apply pointwise to elements of each operand tensor. - - | `ReLU(X)` := `max(X, 0)` - | `tanh(X)` := hyperbolic tangent of `X` - | `sigmoid(X)` := `1 / (1 + exp(-X))` - | `exp(X)` := `e^X` - | `A.B` denotes matrix multiplication of `A` and `B` . - | `A*B` denotes pointwise multiplication of `A` and `B` . - - **Equations** - - Depending on the value of RNNOperation chosen, each sub-layer of the RNN layer will perform one of the following operations: - - **RELU** - - :math:`H[t] := ReLU(W[i].X[t] + R[i].H[t-1] + Wb[i] + Rb[i])` - - **TANH** - - :math:`H[t] := tanh(W[i].X[t] + R[i].H[t-1] + Wb[i] + Rb[i])` - - **LSTM** - - | :math:`i[t] := sigmoid(W[i].X[t] + R[i].H[t-1] + Wb[i] + Rb[i])` - | :math:`f[t] := sigmoid(W[f].X[t] + R[f].H[t-1] + Wb[f] + Rb[f])` - | :math:`o[t] := sigmoid(W[o].X[t] + R[o].H[t-1] + Wb[o] + Rb[o])` - | :math:`c[t] := tanh(W[c].X[t] + R[c].H[t-1] + Wb[c] + Rb[c])` - - - | :math:`C[t] := f[t]*C[t-1] + i[t]*c[t]` - | :math:`H[t] := o[t]*tanh(C[t])` - - **GRU** - - | :math:`z[t] := sigmoid(W[z].X[t] + R[z].H[t-1] + Wb[z] + Rb[z])` - | :math:`r[t] := sigmoid(W[r].X[t] + R[r].H[t-1] + Wb[r] + Rb[r])` - | :math:`h[t] := tanh(W[h].X[t] + r[t]*(R[h].H[t-1] + Rb[h]) + Wb[h])` - | :math:`H[t] := (1 - z[t])*h[t] + z[t]*H[t-1]` - )trtdoc"; - - constexpr const char* RELU = R"trtdoc(Single gate RNN w/ ReLU activation)trtdoc"; - constexpr const char* TANH = R"trtdoc(Single gate RNN w/ TANH activation)trtdoc"; - constexpr const char* LSTM = R"trtdoc(Four-gate LSTM network w/o peephole connections)trtdoc"; - constexpr const char* GRU = R"trtdoc(Three-gate network consisting of Gated Recurrent Units)trtdoc"; - - } // RNNOperationDoc - - namespace RNNDirectionDoc - { - constexpr const char* descr = R"trtdoc(The RNN direction that may be performed by an RNN layer.)trtdoc"; - - constexpr const char* UNIDIRECTION = R"trtdoc(Network iterates from first input to last input)trtdoc"; - constexpr const char* BIDIRECTION = R"trtdoc(Network iterates from first to last (and vice versa) and outputs concatenated)trtdoc"; - } // RNNDirectionDoc - - namespace RNNInputModeDoc - { - constexpr const char* descr = R"trtdoc( - The RNN input modes that may occur with an RNN layer. - - If the RNN is configured with :const:`RNNInputMode.LINEAR` , then for each gate `g` in the first layer of the RNN, - the input vector `X[t]` (length `E`) is left-multiplied by the gate's corresponding weight matrix `W[g]` - (dimensions `HxE`) as usual, before being used to compute the gate output as described by :class:`RNNOperation` . - - If the RNN is configured with :const:`RNNInputMode.SKIP` , then this initial matrix multiplication is "skipped" - and `W[g]` is conceptually an identity matrix. In this case, the input vector `X[t]` must have length `H` - (the size of the hidden state). - )trtdoc"; - - constexpr const char* LINEAR = R"trtdoc(Perform the normal matrix multiplication in the first recurrent layer)trtdoc"; - constexpr const char* SKIP = R"trtdoc(No operation is performed on the first recurrent layer)trtdoc"; - } // RNNInputModeDoc - - namespace IRNNLayerDoc - { - constexpr const char* descr = R"trtdoc( - An RNN layer in an :class:`INetworkDefinition` . - - This layer applies an RNN operation on the inputs. - - **Deprecated** This interface is superseded by IRNNv2Layer. - - :ivar num_layers: :class:`int` The number of layers in the RNN. - :ivar hidden_size: :class:`int` The size of the hidden layers. - :ivar max_seq_length: :class:`int` The sequence length. This is the maximum number of input tensors that the RNN can process at once. - :ivar op: :class:`RNNOperation` The operation of the RNN layer. - :ivar input_mode: :class:`RNNInputMode` The input mode of the RNN layer. - :ivar direction: :class:`RNNDirection` the direction of the RNN layer. The direction determines if the RNN is run as a unidirectional(left to right) or bidirectional(left to right and right to left). In the :const:`RNNDirection.BIDIRECTION` case the output is concatenated together, resulting in output size of 2x :attr:`hidden_size` . - :ivar weights: :class:`Weights` The weight parameters for the RNN. For more information, see `IRNNLayer::setWeights() `_. - :ivar bias: :class:`Weights` The bias parameter vector for the RNN layer. For more information see `IRNNLayer::setBias() `_. - :ivar data_length: :class:`int` The length of the data being processed by the RNN for use in computing other values. - :ivar hidden_state: :class:`ITensor` the initial hidden state of the RNN with the provided hidden ITensor. - The layout for hidden is a linear layout of a 3D matrix: - C - The number of layers in the RNN, it must match :attr:`num_layers` . - H - The number of mini-batches for each time sequence. - W - The size of the per layer hidden states, it must match :attr:`hidden_size` . - The amount of space required is doubled if :attr:`direction` is :const:`RNNDirection.BIDIRECTION` with the bidirectional states coming after the unidirectional states. - If not specified, then the initial hidden state is set to zero. - - :ivar cell_state: :class:`ITensor` the initial cell state of the RNN with the provided cell ITensor. - The layout for cell is a linear layout of a 3D matrix: - C - The number of layers in the RNN, it must match :attr:`num_layers` . - H - The number of mini-batches for each time sequence. - W - The size of the per layer hidden states, it must match :attr:`hidden_size` . - The amount of space required is doubled if :attr:`direction` is :const:`RNNDirection.BIDIRECTION` with the bidirectional states coming after the unidirectional states. - If not specified, then the initial cell state is set to zero. - The cell state only affects LSTM RNN's. - )trtdoc"; - } // IRNNLayerDoc - - namespace RNNGateTypeDoc - { - constexpr const char* descr = R"trtdoc( - The RNN input modes that may occur with an RNN layer. - - If the RNN is configured with :const:`RNNInputMode.LINEAR` , then for each gate `g` in the first layer of the RNN, - the input vector `X[t]` (length `E`) is left-multiplied by the gate's corresponding weight matrix `W[g]` - (dimensions `HxE`) as usual, before being used to compute the gate output as described by :class:`RNNOperation` . - - If the RNN is configured with :const:`RNNInputMode.SKIP` , then this initial matrix multiplication is "skipped" - and `W[g]` is conceptually an identity matrix. In this case, the input vector `X[t]` must have length `H` - (the size of the hidden state). - )trtdoc"; - - constexpr const char* INPUT = R"trtdoc(Input Gate)trtdoc"; - constexpr const char* OUTPUT = R"trtdoc(Output Gate)trtdoc"; - constexpr const char* FORGET = R"trtdoc(Forget Gate)trtdoc"; - constexpr const char* UPDATE = R"trtdoc(Update Gate)trtdoc"; - constexpr const char* RESET = R"trtdoc(Reset Gate)trtdoc"; - constexpr const char* CELL = R"trtdoc(Cell Gate)trtdoc"; - constexpr const char* HIDDEN = R"trtdoc(Hidden Gate)trtdoc"; - } // RNNGateTypeDoc - - namespace IRNNv2LayerDoc - { - constexpr const char* descr = R"trtdoc( - An RNN layer in an :class:`INetworkDefinition` , version 2 - - :ivar num_layers: :class:`int` The layer count of the RNN. - :ivar hidden_size: :class:`int` The hidden size of the RNN. - :ivar max_seq_length: :class:`int` The maximum sequence length of the RNN - :ivar data_length: :class:`int` The length of the data being processed by the RNN for use in computing other values. - - :ivar seq_lengths: :class:`ITensor` Individual sequence lengths in the batch with the :class:`ITensor` provided. - The :attr:`seq_lengths` :class:`ITensor` should be a {N1, ..., Np} tensor, where N1..Np are the index dimensions - of the input tensor to the RNN. - If :attr:`seq_lengths` is not specified, then the RNN layer assumes all sequences are size :attr:`max_seq_length` . - All sequence lengths in :attr:`seq_lengths` should be in the range [1, :attr:`max_seq_length` ]. Zero-length sequences are not supported. - This tensor must be of type int32. - :ivar op: :class:`RNNOperation` The operation of the RNN layer. - :ivar input_mode: :class:`int` The input mode of the RNN layer. - :ivar direction: :class:`int` The direction of the RNN layer. - - :ivar hidden_state: :class:`ITensor` the initial hidden state of the RNN with the provided :attr:`hidden_state` :class:`ITensor` . - The :attr:`hidden_state` :class:`ITensor` should have the dimensions `{N1, ..., Np, L, H}`, where: - `N1..Np` are the index dimensions specified by the input tensor - `L` is the number of layers in the RNN, equal to :attr:`num_layers` - `H` is the hidden state for each layer, equal to :attr:`hidden_size` if :attr:`direction` is :const:`RNNDirection.UNIDIRECTION` , and 2x :attr:`hidden_size` otherwise. - :ivar cell_state: :class:`ITensor` The initial cell state of the LSTM with the provided :attr:`cell_state` :class:`ITensor` . - The :attr:`cell_state` :class:`ITensor` should have the dimensions `{N1, ..., Np, L, H}`, where: - `N1..Np` are the index dimensions specified by the input tensor - `L` is the number of layers in the RNN, equal to :attr:`num_layers` - `H` is the hidden state for each layer, equal to :attr:`hidden_size` if :attr:`direction` is :const:`RNNDirection.UNIDIRECTION`, and 2x :attr:`hidden_size` otherwise. - It is an error to set this on an RNN layer that is not configured with :const:`RNNOperation.LSTM` . - )trtdoc"; - - constexpr const char* set_weights_for_gate = R"trtdoc( - Set the weight parameters for an individual gate in the RNN. - - :arg layer_index: The index of the layer that contains this gate. Refer to :attr:`IRNNLayer.weights` for a description of the layer index. - :arg gate: The name of the gate within the RNN layer. The gate name must correspond to one of the gates used by this layer's :class:`RNNOperation` . - :arg is_w: True if the weight parameters are for the input matrix W[g] and false if they are for the recurrent input matrix R[g]. See :class:`RNNOperation` for equations showing how these matrices are used in the RNN gate. - :arg weights: The weight structure holding the weight parameters, which are stored as a row-major 2D matrix. Refer to :attr:`IRNNLayer.weights` for documentation on the expected dimensions of this matrix. - )trtdoc"; - constexpr const char* get_weights_for_gate = R"trtdoc( - Get the weight parameters for an individual gate in the RNN. - - :arg layer_index: The index of the layer that contains this gate. - :arg gate: The name of the gate within the RNN layer. - :arg is_w: True if the weight parameters are for the input matrix W[g] and false if they are for the recurrent input matrix R[g]. - - :returns: The weight parameters. - )trtdoc"; - constexpr const char* set_bias_for_gate = R"trtdoc( - Set the bias parameters for an individual gate in the RNN. - - :arg layer_index: The index of the layer that contains this gate. Refer to :attr:`IRNNLayer.weights` for a description of the layer index. - :arg gate: The name of the gate within the RNN layer. The gate name must correspond to one of the gates used by this layer's :class:`RNNOperation` . - :arg is_w: True if the bias parameters are for the input bias Wb[g] and false if they are for the recurrent input bias Rb[g]. See - :class:`RNNOperation` for equations showing how these bias vectors are used in the RNN gate. - :arg bias: The weight structure holding the bias parameters, which should be an array of size :attr:`hidden_size` . - )trtdoc"; - constexpr const char* get_bias_for_gate = R"trtdoc( - Get the bias parameters for an individual gate in the RNN. - - :arg layer_index: The index of the layer that contains this gate. - :arg gate: The name of the gate within the RNN layer. - :arg is_w: True if the bias parameters are for the input bias Wb[g] and false if they are for the recurrent input bias Rb[g]. - - :returns: The bias parameters. - )trtdoc"; - } // IRNNv2LayerDoc - - namespace IOutputDimensionsFormulaDoc - { - constexpr const char* descr = R"trtdoc( - Application-implemented interface to compute layer output sizes. - )trtdoc"; - - constexpr const char* compute = R"trtdoc( - Application-implemented interface to compute the HW output dimensions of a layer from the layer input and parameters. - - :arg input_shape: The input shape of the layer. - :arg kernel_shape: The kernel shape (or window size, for a pooling layer) parameter of the layer operation. - :arg stride: The stride parameter for the layer. - :arg padding: The padding parameter of the layer. - :arg dilation: The dilation parameter of the layer (only applicable to convolutions). - :arg layer_name: The name of the layer. - - :returns: The output size of the layer - )trtdoc"; - } // IOutputDimensionsFormulaDoc - - namespace IPluginLayerDoc - { - constexpr const char* descr = R"trtdoc( - A plugin layer in an :class:`INetworkDefinition` . - - :ivar plugin: :class:`IPlugin` The plugin for the layer. - )trtdoc"; - } // IPluginLayerDoc - - namespace IPluginV2LayerDoc - { - constexpr const char* descr = R"trtdoc( - A plugin layer in an :class:`INetworkDefinition` . - - :ivar plugin: :class:`IPluginV2` The plugin for the layer. - )trtdoc"; - } // IPluginV2LayerDoc - - namespace UnaryOperationDoc - { - constexpr const char* descr = R"trtdoc(The unary operations that may be performed by a Unary layer.)trtdoc"; - - constexpr const char* EXP = R"trtdoc(Exponentiation)trtdoc"; - constexpr const char* LOG = R"trtdoc(Log (base e))trtdoc"; - constexpr const char* SQRT = R"trtdoc(Square root)trtdoc"; - constexpr const char* RECIP = R"trtdoc(Reciprocal)trtdoc"; - constexpr const char* ABS = R"trtdoc(Absolute value)trtdoc"; - constexpr const char* NEG = R"trtdoc(Negation)trtdoc"; - constexpr const char* SIN = R"trtdoc(Sine)trtdoc"; - constexpr const char* COS = R"trtdoc(Cosine)trtdoc"; - constexpr const char* TAN = R"trtdoc(Tangent)trtdoc"; - constexpr const char* SINH = R"trtdoc(Hyperbolic sine)trtdoc"; - constexpr const char* COSH = R"trtdoc(Hyperbolic cosine)trtdoc"; - constexpr const char* ASIN = R"trtdoc(Inverse sine)trtdoc"; - constexpr const char* ACOS = R"trtdoc(Inverse cosine)trtdoc"; - constexpr const char* ATAN = R"trtdoc(Inverse tangent)trtdoc"; - constexpr const char* ASINH = R"trtdoc(Inverse hyperbolic sine)trtdoc"; - constexpr const char* ACOSH = R"trtdoc(Inverse hyperbolic cosine)trtdoc"; - constexpr const char* ATANH = R"trtdoc(Inverse hyperbolic tangent)trtdoc"; - constexpr const char* CEIL = R"trtdoc(Ceiling)trtdoc"; - constexpr const char* FLOOR = R"trtdoc(Floor)trtdoc"; - constexpr const char* ERF = R"trtdoc(Gauss error function)trtdoc"; - constexpr const char* NOT = R"trtdoc(Not)trtdoc"; - } // UnaryOperationDoc - - namespace IUnaryLayerDoc - { - constexpr const char* descr = R"trtdoc( - A unary layer in an :class:`INetworkDefinition` . - - :ivar op: :class:`UnaryOperation` The unary operation for the layer. - )trtdoc"; - } // IUnaryLayerDoc - - namespace ReduceOperationDoc - { - constexpr const char* descr = R"trtdoc(The reduce operations that may be performed by a Reduce layer)trtdoc"; - - constexpr const char* SUM = R"trtdoc()trtdoc"; - constexpr const char* PROD = R"trtdoc()trtdoc"; - constexpr const char* MAX = R"trtdoc()trtdoc"; - constexpr const char* MIN = R"trtdoc()trtdoc"; - constexpr const char* AVG = R"trtdoc()trtdoc"; - } // ReduceOperationDoc - - namespace IReduceLayerDoc - { - constexpr const char* descr = R"trtdoc( - A reduce layer in an :class:`INetworkDefinition` . - - :ivar op: :class:`ReduceOperation` The reduce operation for the layer. - :ivar axes: :class:`int` The axes over which to reduce. - :ivar keep_dims: :class:`bool` Specifies whether or not to keep the reduced dimensions for the layer. - )trtdoc"; - } // IReduceLayerDoc - - namespace IPaddingLayerDoc - { - constexpr const char* descr = R"trtdoc( - A padding layer in an :class:`INetworkDefinition` . - - :ivar pre_padding: :class:`DimsHW` The padding that is applied at the start of the tensor. Negative padding results in trimming the edge by the specified amount. - :ivar post_padding: :class:`DimsHW` The padding that is applied at the end of the tensor. Negative padding results in trimming the edge by the specified amount - :ivar pre_padding_nd: :class:`Dims` The padding that is applied at the start of the tensor. Negative padding results in trimming the edge by the specified amount. Only 2 dimensions currently supported. - :ivar post_padding_nd: :class:`Dims` The padding that is applied at the end of the tensor. Negative padding results in trimming the edge by the specified amount. Only 2 dimensions currently supported. - )trtdoc"; - } // IPaddingLayerDoc - - namespace PermutationDoc - { - constexpr const char* descr = R"trtdoc( - The elements of the permutation. The permutation is applied as outputDimensionIndex = permutation[inputDimensionIndex], so to permute from CHW order to HWC order, the required permutation is [1, 2, 0], and to permute from HWC to CHW, the required permutation is [2, 0, 1]. - - It supports iteration and indexing and is implicitly convertible to/from Python iterables (like :class:`tuple` or :class:`list` ). Therefore, you can use those classes in place of :class:`Permutation` . - )trtdoc"; - } // PermutationDoc - - namespace IShuffleLayerDoc - { - constexpr const char* descr = R"trtdoc( - A shuffle layer in an :class:`INetworkDefinition` . - - This class shuffles data by applying in sequence: a transpose operation, a reshape operation and a second transpose operation. The dimension types of the output are those of the reshape dimension. - - :ivar first_transpose: :class:`Permutation` The permutation applied by the first transpose operation. Default: Identity Permutation - :ivar reshape_dims: :class:`Dims` The reshaped dimensions. - Two special values can be used as dimensions. - Value 0 copies the corresponding dimension from input. This special value can be used more than once in the dimensions. If number of reshape dimensions is less than input, 0s are resolved by aligning the most significant dimensions of input. - Value -1 infers that particular dimension by looking at input and rest of the reshape dimensions. Note that only a maximum of one dimension is permitted to be specified as -1. - The product of the new dimensions must be equal to the product of the old. - :ivar second_transpose: :class:`Permutation` The permutation applied by the second transpose operation. Default: Identity Permutation - :ivar zero_is_placeholder: :class:`bool` The meaning of 0 in reshape dimensions. - If true, then a 0 in the reshape dimensions denotes copying the corresponding - dimension from the first input tensor. If false, then a 0 in the reshape - dimensions denotes a zero-length dimension. - )trtdoc"; - - constexpr const char* set_input = R"trtdoc( - Sets the input tensor for the given index. The index must be 0 for a static shuffle layer. - A static shuffle layer is converted to a dynamic shuffle layer by calling :func:`set_input` with an index 1. - A dynamic shuffle layer cannot be converted back to a static shuffle layer. - - For a dynamic shuffle layer, the values 0 and 1 are valid. - The indices in the dynamic case are as follows: - - | Index | Description - | 0 | Data or Shape tensor to be shuffled. - | 1 | The dimensions for the reshape operation, as a 1D Int32 shape tensor. - - If this function is called with a value 1, then :attr:`num_inputs` changes - from 1 to 2. - - :arg index: The index of the input tensor. - :arg tensor: The input tensor. - )trtdoc"; - - } // IShuffleLayerDoc - - namespace ISliceLayerDoc - { - constexpr const char* descr = R"trtdoc( - A slice layer in an :class:`INetworkDefinition` . - - :ivar start: :class:`Dims` The start offset. - :ivar shape: :class:`Dims` The output dimensions. - :ivar stride: :class:`Dims` The slicing stride. - :ivar mode: :class:`SliceMode` Controls how ISliceLayer handles out of bounds coordinates. - )trtdoc"; - - constexpr const char* set_input = R"trtdoc( - Sets the input tensor for the given index. The index must be 0 for a static slice layer. - A static slice layer is converted to a dynamic slice layer by calling :func:`set_input` with an index > 0. - A dynamic slice layer cannot be converted back to a static slice layer. - - For a dynamic slice layer, the values 0-3 are valid. If an index > 0 is specified, all values between - index 0 and that index must be dynamic tensors. The values larger than index can use static dimensions. - For example, if an index of two is specified, the stride tensor can be set via setStride, but the start tensor - must be specified via :func:`set_input` as both size and start are converted to dynamic tensors. - The indices in the dynamic case are as follows: - - | Index | Description - | 0 | Data or Shape tensor to be sliced. - | 1 | The start tensor to begin slicing, N-dimensional for Data, and 1-D for Shape. - | 2 | The size tensor of the resulting slice, N-dimensional for Data, and 1-D for Shape. - | 3 | The stride of the slicing operation, N-dimensional for Data, and 1-D for Shape. - - If this function is called with a value greater than 0, then :attr:`num_inputs` changes - from 1 to index + 1. When converting from static to dynamic slice layer, - all unset tensors, between 1 and index + 1, are initialized to nullptr. It is an error to attempt to build - a network that has any nullptr inputs. - - :arg index: The index of the input tensor. - :arg tensor: The input tensor. - )trtdoc"; - - - } // ISliceLayerDoc - - namespace SliceModeDoc - { - constexpr const char* descr = R"trtdoc(Controls how ISliceLayer handles out of bounds coordinates)trtdoc"; - - constexpr const char* DEFAULT - = R"trtdoc(Fail with error when the coordinates are out of bounds. This is the default.)trtdoc"; - constexpr const char* WRAP = R"trtdoc(Coordinates wrap around periodically)trtdoc"; - } // SliceModeDoc - - namespace IShapeLayerDoc - { - constexpr const char* descr = R"trtdoc( - A shape layer in an :class:`INetworkDefinition` . Used for getting the shape of a tensor. - This class sets the output to a one-dimensional tensor with the dimensions of the input tensor. - - For example, if the input is a four-dimensional tensor (of any type) with - dimensions [2,3,5,7], the output tensor is a one-dimensional Int32 tensor - of length 4 containing the sequence 2, 3, 5, 7. - )trtdoc"; - - } // IShapeLayerDoc - - namespace TopKOperationDoc - { - constexpr const char* descr = R"trtdoc(The operations that may be performed by a TopK layer)trtdoc"; - - constexpr const char* MAX = R"trtdoc(Maximum of the elements)trtdoc"; - constexpr const char* MIN = R"trtdoc(Minimum of the elements)trtdoc"; - } // TopKOperationDoc - - namespace ITopKLayerDoc - { - constexpr const char* descr = R"trtdoc( - A TopK layer in an :class:`INetworkDefinition` . - - :ivar op: :class:`TopKOperation` The operation for the layer. - :ivar k: :class:`TopKOperation` the k value for the layer. Currently only values up to 25 are supported. - :ivar axes: :class:`TopKOperation` The axes along which to reduce. - )trtdoc"; - } // ITopKLayerDoc - - namespace MatrixOperationDoc - { - constexpr const char* descr = R"trtdoc(The matrix operations that may be performed by a Matrix layer)trtdoc"; +namespace IFillLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A fill layer in an :class:`INetworkDefinition` . +)trtdoc"; - constexpr const char* NONE = R"trtdoc()trtdoc"; - constexpr const char* TRANSPOSE = R"trtdoc(Transpose each matrix)trtdoc"; - constexpr const char* VECTOR = R"trtdoc(Treat operand as collection of vectors)trtdoc"; - } // MatrixOperationDoc +constexpr const char* set_dimensions = R"trtdoc( + set the output tensor's dimensions. - namespace IMatrixMultiplyLayerDoc - { - constexpr const char* descr = R"trtdoc( - A matrix multiply layer in an :class:`INetworkDefinition` . + :arg dims: the output tensor's dimensions. +)trtdoc"; - Let A be op(getInput(0)) and B be op(getInput(1)) where - op(x) denotes the corresponding MatrixOperation. +constexpr const char* get_dimensions = R"trtdoc( + get the output tensor's dimensions. +)trtdoc"; - When A and B are matrices or vectors, computes the inner product A * B: +constexpr const char* set_operation = R"trtdoc( + set the fill operation for the layer. - | matrix * matrix -> matrix - | matrix * vector -> vector - | vector * matrix -> vector - | vector * vector -> scalar + :arg operation: the fill operation for the layer. +)trtdoc"; - Inputs of higher rank are treated as collections of matrices or vectors. - The output will be a corresponding collection of matrices, vectors, or scalars. +constexpr const char* get_operation = R"trtdoc( + get the fill operation for the layer. +)trtdoc"; - :ivar op0: :class:`MatrixOperation` How to treat the first input. - :ivar op1: :class:`MatrixOperation` How to treat the second input. - )trtdoc"; - } // IMatrixMultiplyLayerDoc +constexpr const char* set_alpha = R"trtdoc( + set the alpha parameter (must be finite). - namespace IRaggedSoftMaxLayerDoc - { - constexpr const char* descr = R"trtdoc( - A ragged softmax layer in an :class:`INetworkDefinition` . + ============== ================== + Operation Usage + ============== ================== + kLINSPACE the start value; + kRANDOMUNIFORM the minimum value; + ============== ================== - This layer takes a ZxS input tensor and an additional Zx1 bounds tensor holding the lengths of the Z sequences. + :arg alpha: has different meanings for each operators. +)trtdoc"; - This layer computes a softmax across each of the Z sequences. +constexpr const char* get_alpha = R"trtdoc( + get the alpha parameter. + see :meth:`IFillLayer.set_alpha()` for details +)trtdoc"; - The output tensor is of the same size as the input tensor. - )trtdoc"; - } // IRaggedSoftMaxLayerDoc +constexpr const char* set_beta = R"trtdoc( + set the beta parameter (must be finite). - namespace IIdentityLayerDoc - { - constexpr const char* descr = R"trtdoc( - A layer that represents the identity function. + =============== ================== + Operation Usage + =============== =================== + kLINSPACE the delta value; + kRANDOMUNIFORM the maximal value; + =============== =================== + + :arg beta: has different meanings for each operators. +)trtdoc"; + +constexpr const char* get_beta = R"trtdoc( + get the beta parameter. + see :meth:`IFillLayer.set_beta()` for details +)trtdoc"; + +constexpr const char* set_input = R"trtdoc( + replace an input of this layer with a specific tensor. - If tensor precision is explicitly specified, it can be used to transform from one precision to another. - )trtdoc"; - } // IIdentityLayerDoc + ===== ========================================================================================================== + Index Description for kLINSPACE + ===== ========================================================================================================== + 0 Shape tensor, represents the output tensor's dimensions. + 1 Start, a scalar, represents the start value. + 2 Delta, a 1D tensor, length equals to shape tensor's nbDims, represents the delta value for each dimension. + ===== ========================================================================================================== + + ===== ======================================================== + Index Description for kRANDOM_UNIFORM + ===== ======================================================== + 0 Shape tensor, represents the output tensor's dimensions. + 1 Minimum, a scalar, represents the minimum random value. + 2 Maximum, a scalar, represents the maximal random value. + ===== ======================================================== + + :arg index: the index of the input to modify. + :arg tensor: the input tensor. +)trtdoc"; +} // namespace IFillLayerDoc + +namespace IQuantizeLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A Quantize layer in an :class:`INetworkDefinition` . + + This layer accepts a floating-point data input tensor, and uses the scale and zeroPt inputs to + + quantize the data to an 8-bit signed integer according to: + + :math:`output = clamp(round(input / scale) + zeroPt)` + + Rounding type is rounding-to-nearest ties-to-even (https://en.wikipedia.org/wiki/Rounding#Round_half_to_even). + + Clamping is in the range [-128, 127]. + + The first input (index 0) is the tensor to be quantized. + The second (index 1) and third (index 2) are the scale and zero point respectively. + Each of scale and zeroPt must be either a scalar, or a 1D tensor. + + The zeroPt tensor is optional, and if not set, will be assumed to be zero. Its data type must be + tensorrt.int8. zeroPt must only contain zero-valued coefficients, because only symmetric quantization is + supported. + The scale value must be either a scalar for per-tensor quantization, or a 1D tensor for per-axis + quantization. The size of the 1-D scale tensor must match the size of the quantization axis. The size of the + scale must match the size of the zeroPt. + + The subgraph which terminates with the scale tensor must be a build-time constant. The same restrictions apply + to the zeroPt. + The output type, if constrained, must be constrained to tensorrt.int8. The input type, if constrained, must be + constrained to tensorrt.float32 (FP16 input is not supported). + The output size is the same as the input size. + + IQuantizeLayer only supports tensorrt.float32 precision and will default to this precision during instantiation. + IQuantizeLayer only supports tensorrt.int8 output. + + :ivar axis: :class:`int` The axis along which quantization occurs. The quantization axis is in reference to the input tensor's dimensions. +)trtdoc"; +} // namespace IQuantizeLayerDoc + +namespace IDequantizeLayerDoc +{ +constexpr const char* descr = R"trtdoc( + A Dequantize layer in an :class:`INetworkDefinition` . + + This layer accepts a signed 8-bit integer input tensor, and uses the configured scale and zeroPt inputs to + dequantize the input according to: + :math:`output = (input - zeroPt) * scale` + + The first input (index 0) is the tensor to be quantized. + The second (index 1) and third (index 2) are the scale and zero point respectively. + Each of scale and zeroPt must be either a scalar, or a 1D tensor. + + The zeroPt tensor is optional, and if not set, will be assumed to be zero. Its data type must be + tensorrt.int8. zeroPt must only contain zero-valued coefficients, because only symmetric quantization is + supported. + The scale value must be either a scalar for per-tensor quantization, or a 1D tensor for per-axis + quantization. The size of the 1-D scale tensor must match the size of the quantization axis. The size of the + scale must match the size of the zeroPt. + + The subgraph which terminates with the scale tensor must be a build-time constant. The same restrictions apply + to the zeroPt. + The output type, if constrained, must be constrained to tensorrt.int8. The input type, if constrained, must be + constrained to tensorrt.float32 (FP16 input is not supported). + The output size is the same as the input size. + + IDequantizeLayer only supports tensorrt.int8 precision and will default to this precision during instantiation. + IDequantizeLayer only supports tensorrt.float32 output. + + :ivar axis: :class:`int` The axis along which dequantization occurs. The dequantization axis is in reference to the input tensor's dimensions. + +)trtdoc"; +} // namespace IDequantizeLayerDoc + +namespace INetworkDefinitionDoc +{ +constexpr const char* descr = R"trtdoc( + Represents a TensorRT Network from which the Builder can build an Engine + + :ivar num_layers: :class:`int` The number of layers in the network. + :ivar num_inputs: :class:`int` The number of inputs of the network. + :ivar num_outputs: :class:`int` The number of outputs of the network. + :ivar name: :class:`str` The name of the network. This is used so that it can be associated with a built engine. The name must be at most 128 characters in length. TensorRT makes no use of this string except storing it as part of the engine so that it may be retrieved at runtime. A name unique to the builder will be generated by default. + :ivar has_implicit_batch_dimension: :class:`bool` Whether the network was created with an implicit batch dimension. This is a network-wide property. Either all tensors in the network have an implicit batch dimension or none of them do. This is True when the INetworkDefinition is created with default flags: ``create_network()``. To specify explicit batch, set the flag: ``create_network(flags=1 << int(tensorrt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))``. + :ivar has_explicit_precision: :class:`bool` True if and only if this :class:`INetworkDefinition` was created with ``NetworkDefinitionCreationFlag.EXPLICIT_PRECISION`` set: ``create_network(flags=(1 << int(NetworkDefinitionCreationFlag.EXPLICIT_PRECISION)))``. + :ivar error_recorder: :class:`IErrorRecorder` Application-implemented error reporting interface for TensorRT objects. +)trtdoc"; - namespace IConstantLayerDoc - { - constexpr const char* descr = R"trtdoc( - A constant layer in an :class:`INetworkDefinition` . +constexpr const char* add_input = R"trtdoc( + Adds an input to the network. - Note: This layer does not support boolean types. + :arg name: The name of the tensor. + :arg dtype: The data type of the tensor. Currently, tensorrt.int8 is not supported for inputs. + :arg shape: The dimensions of the tensor. The total volume must be less than 2^30 elements. - :ivar weights: :class:`Weights` The weights for the layer. - :ivar shape: :class:`Dims` The shape of the layer. - )trtdoc"; - } // IConstantLayerDoc + :returns: The newly added Tensor. +)trtdoc"; - namespace IParametricReLULayerDoc - { - constexpr const char* descr = R"trtdoc( - A parametric ReLU layer in an :class:`INetworkDefinition` . +constexpr const char* mark_output = R"trtdoc( + Mark a tensor as an output. - This layer applies a parametric ReLU activation to an input tensor (first input), with slopes taken from a - slopes tensor (second input). This can be viewed as a leaky ReLU operation where the negative slope differs - from element to element (and can in fact be learned). + :arg tensor: The tensor to mark. +)trtdoc"; - The slopes tensor must be unidirectional broadcastable to the input tensor: the rank of the two tensors must - be the same, and all dimensions of the slopes tensor must either equal the input tensor or be 1. - The output tensor has the same shape as the input tensor. - )trtdoc"; - } // IParametricReLULayerDoc +constexpr const char* add_convolution = R"trtdoc( + Add a 2D convolution layer to the network. + See :class:`IConvolutionLayer` for more information. - namespace ResizeModeDoc - { - constexpr const char* descr = R"trtdoc(Various modes of resize in the resize layer.)trtdoc"; + :arg input: The input tensor to the convolution. + :arg num_output_maps: The number of output feature maps for the convolution. + :arg kernel_shape: The dimensions of the convolution kernel. + :arg kernel: The kernel weights for the convolution. + :arg bias: The optional bias weights for the convolution. + + :returns: The new convolution layer, or :class:`None` if it could not be created. +)trtdoc"; + +constexpr const char* add_convolution_nd = R"trtdoc( + Add a multi-dimension convolution layer to the network. + See :class:`IConvolutionLayer` for more information. + + :arg input: The input tensor to the convolution. + :arg num_output_maps: The number of output feature maps for the convolution. + :arg kernel_shape: The dimensions of the convolution kernel. + :arg kernel: The kernel weights for the convolution. + :arg bias: The optional bias weights for the convolution. + + :returns: The new convolution layer, or :class:`None` if it could not be created. +)trtdoc"; + +constexpr const char* add_fully_connected = R"trtdoc( + Add a fully connected layer to the network. + See :class:`IFullyConnectedLayer` for more information. + + :arg input: The input tensor to the layer. + :arg num_outputs: The number of outputs of the layer. + :arg kernel: The kernel weights for the convolution. + :arg bias: The optional bias weights for the convolution. - constexpr const char* NEAREST = R"trtdoc(1D, 2D, and 3D nearest neighbor resizing.)trtdoc"; - constexpr const char* LINEAR = R"trtdoc(Can handle linear, bilinear, trilinear resizing.)trtdoc"; - } /* ResizeModeDoc */ + :returns: The new fully connected layer, or :class:`None` if it could not be created. +)trtdoc"; - namespace IResizeLayerDoc - { - constexpr const char* descr = R"trtdoc( - A resize layer in an :class:`INetworkDefinition` . +constexpr const char* add_activation = R"trtdoc( + Add an activation layer to the network. + See :class:`IActivationLayer` for more information. - Resize layer can be used for resizing a N-D tensor. + :arg input: The input tensor to the layer. + :arg type: The type of activation function to apply. - Resize layer currently supports the following configurations: + :returns: The new activation layer, or :class:`None` if it could not be created. +)trtdoc"; - * ResizeMode.NEAREST - resizes innermost `m` dimensions of N-D, where 0 < m <= min(3, N) and N > 0. - * ResizeMode.LINEAR - resizes innermost `m` dimensions of N-D, where 0 < m <= min(3, N) and N > 0. +constexpr const char* add_pooling = R"trtdoc( + Add a 2D pooling layer to the network. + See :class:`IPoolingLayer` for more information. - Default resize mode is ResizeMode.NEAREST. + :arg input: The input tensor to the layer. + :arg type: The type of pooling to apply. + :arg window_size: The size of the pooling window. - Resize layer provides two ways to resize tensor dimensions: + :returns: The new pooling layer, or :class:`None` if it could not be created. +)trtdoc"; - * Set output dimensions directly. It can be done for static as well as dynamic resize layer. - Static resize layer requires output dimensions to be known at build-time. - Dynamic resize layer requires output dimensions to be set as one of the input tensors. +constexpr const char* add_pooling_nd = R"trtdoc( + Add a multi-dimension pooling layer to the network. + See :class:`IPoolingLayer` for more information. - * Set scales for resize. Each output dimension is calculated as floor(input dimension * scale). - Only static resize layer allows setting scales where the scales are known at build-time. + :arg input: The input tensor to the layer. + :arg type: The type of pooling to apply. + :arg window_size: The size of the pooling window. - :ivar shape: :class:`Dims` The output dimensions. Must to equal to input dimensions size. - :ivar scales: :class:`List[float]` List of resize scales. - :ivar resize_mode: :class:`ResizeMode` Resize mode can be Linear or Nearest. - :ivar align_corners: :class:`bool` If True, the centers of the 4 corner pixels of both input and output tensors are aligned. Default: False. - )trtdoc"; + :returns: The new pooling layer, or :class:`None` if it could not be created. +)trtdoc"; - constexpr const char* set_input = R"trtdoc( - Sets the input tensor for the given index. +constexpr const char* add_lrn = R"trtdoc( + Add a LRN layer to the network. + See :class:`ILRNLayer` for more information. - If index == 1 and num_inputs == 1, and there is no implicit batch dimension, - in which case num_inputs changes to 2. - Once such additional input is set, resize layer works in dynamic mode. - When index == 1 and num_inputs == 1, the output dimensions are used from - the input tensor, overriding the dimensions supplied by `shape`. + :arg input: The input tensor to the layer. + :arg window: The size of the window. + :arg alpha: The alpha value for the LRN computation. + :arg beta: The beta value for the LRN computation. + :arg k: The k value for the LRN computation. - :arg index: The index of the input tensor. - :arg tensor: The input tensor. - )trtdoc"; - } /* IResizeLayerDoc */ + :returns: The new LRN layer, or :class:`None` if it could not be created. +)trtdoc"; +constexpr const char* add_scale = R"trtdoc( + Add a scale layer to the network. + See :class:`IScaleLayer` for more information. - namespace LoopOutputDoc { - constexpr const char* descr = R"trtdoc(Describes kinds of loop outputs.)trtdoc"; + :arg input: The input tensor to the layer. This tensor is required to have a minimum of 3 dimensions. + :arg mode: The scaling mode. + :arg shift: The shift value. + :arg scale: The scale value. + :arg power: The power value. - constexpr const char* LAST_VALUE = R"trtdoc(Output value is value of tensor for last iteration.)trtdoc"; - constexpr const char* CONCATENATE = R"trtdoc(Output value is concatenation of values of tensor for each iteration, in forward order.)trtdoc"; - constexpr const char* REVERSE = R"trtdoc(Output value is concatenation of values of tensor for each iteration, in reverse order.)trtdoc"; - } /* LoopOutputDoc */ + If the weights are available, then the size of weights are dependent on the ScaleMode. + For UNIFORM, the number of weights is equal to 1. + For CHANNEL, the number of weights is equal to the channel dimension. + For ELEMENTWISE, the number of weights is equal to the volume of the input. + :returns: The new scale layer, or :class:`None` if it could not be created. +)trtdoc"; - namespace TripLimitDoc { - constexpr const char* descr = R"trtdoc(Describes kinds of trip limits.)trtdoc"; +constexpr const char* add_scale_nd = R"trtdoc( + Add a multi-dimension scale layer to the network. + See :class:`IScaleLayer` for more information. - constexpr const char* COUNT = R"trtdoc(Tensor is scalar of type kINT32 that contains the trip count.)trtdoc"; - constexpr const char* WHILE = R"trtdoc(Tensor is a scalar of type BOOL. Loop terminates when value is false.)trtdoc"; + :arg input: The input tensor to the layer. This tensor is required to have a minimum of 3 dimensions. + :arg mode: The scaling mode. + :arg shift: The shift value. + :arg scale: The scale value. + :arg power: The power value. + :arg channel_axis: The channel dimension axis. - } /* TripLimitDoc */ + If the weights are available, then the size of weights are dependent on the ScaleMode. + For UNIFORM, the number of weights is equal to 1. + For CHANNEL, the number of weights is equal to the channel dimension. + For ELEMENTWISE, the number of weights is equal to the volume of the input. + :returns: The new scale layer, or :class:`None` if it could not be created. +)trtdoc"; - namespace ILoopBoundaryLayerDoc { - constexpr const char* descr = R"trtdoc( - :ivar loop: :class:`ILoop` associated with this boundary layer. - )trtdoc"; +constexpr const char* add_softmax = R"trtdoc( + Add a softmax layer to the network. + See :class:`ISoftMaxLayer` for more information. - } /* ILoopBoundaryLayerDoc */ + :arg input: The input tensor to the layer. + :returns: The new softmax layer, or :class:`None` if it could not be created. +)trtdoc"; - namespace IRecurrenceLayerDoc { - constexpr const char* descr = R"trtdoc()trtdoc"; - constexpr const char* set_input = R"trtdoc( - Set the first or second input. - If index==1 and the number of inputs is one, the input is appended. - The first input specifies the initial output value, and must come from outside the loop. - The second input specifies the next output value, and must come from inside the loop. - The two inputs must have the same dimensions. +constexpr const char* add_concatenation = R"trtdoc( + Add a concatenation layer to the network. Note that all tensors must have the same dimension except for the Channel dimension. + See :class:`IConcatenationLayer` for more information. - :param index: The index of the input to set. - :param tensor: The input tensor. - )trtdoc"; - } /* IRecurrenceLayerDoc */ + :arg inputs: The input tensors to the layer. + :returns: The new concatenation layer, or :class:`None` if it could not be created. +)trtdoc"; - namespace ILoopOutputLayerDoc - { - constexpr const char* descr = R"trtdoc( - An :class:`ILoopOutputLayer` is the sole way to get output from a loop. +constexpr const char* add_deconvolution = R"trtdoc( + Add a 2D deconvolution layer to the network. + See :class:`IDeconvolutionLayer` for more information. - The first input tensor must be defined inside the loop; the output tensor is outside the loop. - The second input tensor, if present, must be defined outside the loop. + :arg input: The input tensor to the layer. + :arg num_output_maps: The number of output feature maps. + :arg kernel_shape: The dimensions of the convolution kernel. + :arg kernel: The kernel weights for the convolution. + :arg bias: The optional bias weights for the convolution. - If :attr:`kind` is ``LAST_VALUE``, a single input must be provided. + :returns: The new deconvolution layer, or :class:`None` if it could not be created. +)trtdoc"; - If :attr:`kind` is ``CONCATENATE`` or ``REVERSE``, a second input must be provided. - The second input must be a scalar “shape tensor”, defined before the loop commences, - that specifies the concatenation length of the output. +constexpr const char* add_deconvolution_nd = R"trtdoc( + Add a multi-dimension deconvolution layer to the network. + See :class:`IDeconvolutionLayer` for more information. - The output tensor has j more dimensions than the input tensor, where - j == 0 if :attr:`kind` is ``LAST_VALUE`` - j == 1 if :attr:`kind` is ``CONCATENATE`` or ``REVERSE``. + :arg input: The input tensor to the layer. + :arg num_output_maps: The number of output feature maps. + :arg kernel_shape: The dimensions of the convolution kernel. + :arg kernel: The kernel weights for the convolution. + :arg bias: The optional bias weights for the convolution. - :ivar axis: The contenation axis. Ignored if :attr:`kind` is ``LAST_VALUE``. - For example, if the input tensor has dimensions [b,c,d], - and :attr:`kind` is ``CONCATENATE``, the output has four dimensions. - Let a be the value of the second input. - axis=0 causes the output to have dimensions [a,b,c,d]. - axis=1 causes the output to have dimensions [b,a,c,d]. - axis=2 causes the output to have dimensions [b,c,a,d]. - axis=3 causes the output to have dimensions [b,c,d,a]. - Default is axis is 0. - :ivar kind: The kind of loop output. See :class:`LoopOutput` - )trtdoc"; + :returns: The new deconvolution layer, or :class:`None` if it could not be created. +)trtdoc"; - constexpr const char* set_input = R"trtdoc( - Like :func:`ILayer.set_input`, but additionally works if index==1, :attr:`num_inputs`==1, in which case :attr:`num_inputs` changes to 2. - )trtdoc"; +constexpr const char* add_elementwise = R"trtdoc( + Add an elementwise layer to the network. + See :class:`IElementWiseLayer` for more information. + :arg input1: The first input tensor to the layer. + :arg input2: The second input tensor to the layer. + :arg op: The binary operation that the layer applies. - } /* ILoopOutputLayerDoc */ + The input tensors must have the same number of dimensions. + For each dimension, their lengths must match, or one of them must be one. + In the latter case, the tensor is broadcast along that axis. + The output tensor has the same number of dimensions as the inputs. + For each dimension, its length is the maximum of the lengths of the + corresponding input dimension. - namespace ITripLimitLayerDoc - { - constexpr const char* descr = R"trtdoc( - :ivar kind: The kind of trip limit. See :class:`TripLimit` - )trtdoc"; - } /* ITripLimitLayerDoc */ + :returns: The new element-wise layer, or :class:`None` if it could not be created. +)trtdoc"; +constexpr const char* add_unary = R"trtdoc( + Add a unary layer to the network. + See :class:`IUnaryLayer` for more information. - namespace IIteratorLayerDoc - { - constexpr const char* descr = R"trtdoc( - :ivar axis: The axis to iterate over - :ivar reverse: For reverse=false, the layer is equivalent to add_gather(tensor, I, 0) where I is a - scalar tensor containing the loop iteration number. - For reverse=true, the layer is equivalent to add_gather(tensor, M-1-I, 0) where M is the trip count - computed from TripLimits of kind ``COUNT``. - The default is reverse=false. - )trtdoc"; - } /* IIteratorLayerDoc */ + :arg input: The input tensor to the layer. + :arg op: The operation to apply. + :returns: The new unary layer, or :class:`None` if it could not be created. +)trtdoc"; - namespace ILoopDoc - { - constexpr const char* descr = R"trtdoc( - Helper for creating a recurrent subgraph. +constexpr const char* add_padding = R"trtdoc( + Add a 2D padding layer to the network. + See :class:`IPaddingLayer` for more information. - :ivar name: The name of the loop. The name is used in error diagnostics. - )trtdoc"; + :arg input: The input tensor to the layer. + :arg pre_padding: The padding to apply to the start of the tensor. + :arg post_padding: The padding to apply to the end of the tensor. - constexpr const char* add_recurrence = R"trtdoc( - Create a recurrence layer for this loop with initial_value as its first input. + :returns: The new padding layer, or :class:`None` if it could not be created. +)trtdoc"; - :param initial_value: The initial value of the recurrence layer. +constexpr const char* add_padding_nd = R"trtdoc( + Add a multi-dimensional padding layer to the network. + See :class:`IPaddingLayer` for more information. - :returns: The added :class:`IRecurrenceLayer` , or :class:`None` if it could not be created. - )trtdoc"; + :arg input: The input tensor to the layer. + :arg pre_padding: The padding to apply to the start of the tensor. + :arg post_padding: The padding to apply to the end of the tensor. - constexpr const char* add_trip_limit = R"trtdoc( - Add a trip-count limiter, based on the given tensor. + :returns: The new padding layer, or :class:`None` if it could not be created. +)trtdoc"; - There may be at most one ``COUNT`` and one ``WHILE`` limiter for a loop. - When both trip limits exist, the loop exits when the - count is reached or condition is falsified. - It is an error to not add at least one trip limiter. +constexpr const char* add_shuffle = R"trtdoc( + Add a shuffle layer to the network. + See :class:`IShuffleLayer` for more information. - For ``WHILE``, the input tensor must be the output of a subgraph that contains - only layers that are not :class:`ITripLimitLayer` , :class:`IIteratorLayer` or :class:`ILoopOutputLayer` . - Any :class:`IRecurrenceLayer` s in the subgraph must belong to the same loop as the - :class:`ITripLimitLayer` . A trivial example of this rule is that the input to the ``WHILE`` - is the output of an :class:`IRecurrenceLayer` for the same loop. + :arg input: The input tensor to the layer. + :returns: The new shuffle layer, or :class:`None` if it could not be created. +)trtdoc"; - :param tensor: The input tensor. Must be available before the loop starts. - :param kind: The kind of trip limit. See :class:`TripLimit` +constexpr const char* add_slice = R"trtdoc( + Add a slice layer to the network. + See :class:`ISliceLayer` for more information. - :returns: The added :class:`ITripLimitLayer` , or :class:`None` if it could not be created. - )trtdoc"; + :arg input: The input tensor to the layer. + :arg start: The start offset. + :arg shape: The output shape. + :arg stride: The slicing stride. Positive, negative, zero stride values, and combinations of them in different dimensions are allowed. - constexpr const char* add_iterator = R"trtdoc( - Return layer that subscripts tensor by loop iteration. + :returns: The new slice layer, or :class:`None` if it could not be created. +)trtdoc"; - For reverse=false, this is equivalent to add_gather(tensor, I, 0) where I is a - scalar tensor containing the loop iteration number. - For reverse=true, this is equivalent to add_gather(tensor, M-1-I, 0) where M is the trip count - computed from TripLimits of kind ``COUNT``. +constexpr const char* add_reduce = R"trtdoc( + Add a reduce layer to the network. + See :class:`IReduceLayer` for more information. - :param tensor: The tensor to iterate over. - :param axis: The axis along which to iterate. - :param reverse: Whether to iterate in the reverse direction. + :arg input: The input tensor to the layer. + :arg op: The reduction operation to perform. + :arg axes: The reduction dimensions. - :returns: The :class:`IIteratorLayer` , or :class:`None` if it could not be created. - )trtdoc"; + | Bit 0 of the uint32_t type corresponds to the non-batch dimension 0 boolean and so on. + | If a bit is set, then the corresponding dimension will be reduced. + | Let's say we have an NCHW tensor as input (three non-batch dimensions). + | Bit 0 corresponds to the C dimension boolean. + | Bit 1 corresponds to the H dimension boolean. + | Bit 2 corresponds to the W dimension boolean. + | Note that reduction is not permitted over the batch size dimension. + :arg keep_dims: The boolean that specifies whether or not to keep the reduced dimensions in the output of the layer. - constexpr const char* add_loop_output = R"trtdoc( - Make an output for this loop, based on the given tensor. + :returns: The new reduce layer, or :class:`None` if it could not be created. +)trtdoc"; - If ``kind`` is ``CONCATENATE`` or ``REVERSE``, a second input specifying the - concatenation dimension must be added via method :func:`ILoopOutputLayer.set_input` . +constexpr const char* add_topk = R"trtdoc( + Add a TopK layer to the network. + See :class:`ITopKLayer` for more information. - :param kind: The kind of loop output. See :class:`LoopOutput` - :param axis: The axis for concatenation (if using ``kind`` of ``CONCATENATE`` or ``REVERSE``). + The TopK layer has two outputs of the same dimensions. The first contains data values, the second contains index positions for the values. Output values are sorted, largest first for operation :const:`TopKOperation.MAX` and smallest first for operation :const:`TopKOperation.MIN` . - :returns: The added :class:`ILoopOutputLayer` , or :class:`None` if it could not be created. - )trtdoc"; + Currently only values of K up to 1024 are supported. - } /* ILoopDoc */ + :arg input: The input tensor to the layer. + :arg op: Operation to perform. + :arg k: Number of elements to keep. + :arg axes: The reduction dimensions. + Bit 0 of the uint32_t type corresponds to the non-batch dimension 0 boolean and so on. + If a bit is set, then the corresponding dimension will be reduced. + Let's say we have an NCHW tensor as input (three non-batch dimensions). + Bit 0 corresponds to the C dimension boolean. + Bit 1 corresponds to the H dimension boolean. + Bit 2 corresponds to the W dimension boolean. + Note that TopK reduction is currently only permitted over one dimension. - namespace ISelectLayerDoc - { - constexpr const char* descr = R"trtdoc( - A select layer in an :class:`INetworkDefinition` . + :returns: The new TopK layer, or :class:`None` if it could not be created. +)trtdoc"; - This layer implements an element-wise ternary conditional operation. Wherever ``condition`` is ``True``, elements are taken from the first input, and wherever ``condition`` is ``False``, elements are taken from the second input. - )trtdoc"; - } /* ISelectLayerDoc */ +constexpr const char* add_gather = R"trtdoc( + Add a pooling layer to the network. + See :class:`IGatherLayer` for more information. - namespace FillOperationDoc - { - constexpr const char* descr = R"trtdoc(The tensor fill operations that may performed by an Fill layer.)trtdoc"; + :arg input: The tensor to gather values from. + :arg indices: The tensor to get indices from to populate the output tensor. + :arg axis: The non-batch dimension axis in the data tensor to gather on. - constexpr const char* LINSPACE = R"trtdoc(Generate evenly spaced numbers over a specified interval)trtdoc"; - constexpr const char* RANDOM_UNIFORM = R"trtdoc(Generate a tensor with random values drawn from a uniform distribution)trtdoc"; - } /* FillOperationDoc */ + :returns: The new pooling layer, or :class:`None` if it could not be created. +)trtdoc"; - namespace IFillLayerDoc - { - constexpr const char* descr = R"trtdoc( - A fill layer in an :class:`INetworkDefinition` . - )trtdoc"; +constexpr const char* add_ragged_softmax = R"trtdoc( + Add a ragged softmax layer to the network. + See :class:`IRaggedSoftMaxLayer` for more information. - constexpr const char* set_dimensions = R"trtdoc( - set the output tensor's dimensions. + :arg input: The ZxS input tensor. + :arg bounds: The Zx1 bounds tensor. - :arg dims: the output tensor's dimensions. - )trtdoc"; + :returns: The new ragged softmax layer, or :class:`None` if it could not be created. +)trtdoc"; - constexpr const char* get_dimensions = R"trtdoc( - get the output tensor's dimensions. - )trtdoc"; +constexpr const char* add_matrix_multiply = R"trtdoc( + Add a matrix multiply layer to the network. + See :class:`IMatrixMultiplyLayer` for more information. - constexpr const char* set_operation = R"trtdoc( - set the fill operation for the layer. + :arg input0: The first input tensor (commonly A). + :arg op0: Whether to treat input0 as matrices, transposed matrices, or vectors. + :arg input1: The second input tensor (commonly B). + :arg op1: Whether to treat input1 as matrices, transposed matrices, or vectors. - :arg operation: the fill operation for the layer. - )trtdoc"; + :returns: The new matrix multiply layer, or :class:`None` if it could not be created. +)trtdoc"; - constexpr const char* get_operation = R"trtdoc( - get the fill operation for the layer. - )trtdoc"; +constexpr const char* add_matrix_multiply_deprecated = R"trtdoc( + Add a matrix multiply layer to the network. + See :class:`IMatrixMultiplyLayer` for more information. - constexpr const char* set_alpha = R"trtdoc( - set the alpha parameter (must be finite). + :arg input0: The first input tensor (commonly A). + :arg transpose0: If true, op(input0)=transpose(input0), else op(input0)=input0. + :arg input1: The second input tensor (commonly B). + :arg transpose1: If true, op(input1)=transpose(input1), else op(input1)=input1. - Operation | Usage - kLINSPACE | the start value; - kRANDOMUNIFORM | the minimum value; + :returns: The new matrix multiply layer, or :class:`None` if it could not be created. +)trtdoc"; - :arg alpha: has different meanings for each operators. - )trtdoc"; +constexpr const char* add_constant = R"trtdoc( + Add a constant layer to the network. + See :class:`IConstantLayer` for more information. - constexpr const char* get_alpha = R"trtdoc( - get the alpha parameter. - see :meth:`IFillLayer.set_alpha()` for details - )trtdoc"; + :arg shape: The shape of the constant. + :arg weights: The constant value, represented as weights. - constexpr const char* set_beta = R"trtdoc( - set the beta parameter (must be finite). + :returns: The new constant layer, or :class:`None` if it could not be created. +)trtdoc"; - Operation | Usage - kLINSPACE | the delta value; - kRANDOMUNIFORM | the maximal value; +constexpr const char* add_rnn_v2 = R"trtdoc( + Add an RNNv2 layer to the network. + See :class:`IRNNv2Layer` for more information. - :arg beta: has different meanings for each operators. - )trtdoc"; + Add an ``layer_count`` deep RNN layer to the network with ``hidden_size`` internal states that can take a batch with fixed or variable sequence lengths. - constexpr const char* get_beta = R"trtdoc( - get the beta parameter. - see :meth:`IFillLayer.set_beta()` for details - )trtdoc"; + :arg input: The input tensor to the layer (see below). + :arg layer_count: The number of layers in the RNN. + :arg hidden_size: Size of the internal hidden state for each layer. + :arg max_seq_length: Maximum sequence length for the input. + :arg op: The type of RNN to execute. - constexpr const char* set_input = R"trtdoc( - replace an input of this layer with a specific tensor. + By default, the layer is configured with :const:`RNNDirection.UNIDIRECTION` and :const:`RNNInputMode.LINEAR` . To change these settings, set :attr:`IRNNv2Layer.direction` and :attr:`IRNNv2Layer.input_mode` . - Index | Description for kLINSPACE - 0 | Shape tensor, represents the output tensor's dimensions. - 1 | Start, a scalar, represents the start value. - 2 | Delta, a 1D tensor, length equals to shape tensor's nbDims, represents the delta value for each dimension. + Weights and biases for the added layer should be set using :meth:`IRNNv2Layer.set_weights_for_gate()` and :meth:`IRNNv2Layer.set_bias_for_gate()` prior to building an engine using this network. - Index | Description for kRANDOM_UNIFORM - 0 | Shape tensor, represents the output tensor's dimensions. - 1 | Minimum, a scalar, represents the minimum random value. - 2 | Maximum, a scalar, represents the maximal random value. + The input tensors must be of the type :const:`float32` or :const:`float16` . + The layout of the weights is row major and must be the same datatype as the input tensor. + ``weights`` contain 8 matrices and ``bias`` contains 8 vectors. - :arg index: the index of the input to modify. - :arg tensor: the input tensor. - )trtdoc"; - } /* IFillLayerDoc */ + See :meth:`IRNNv2Layer.set_weights_for_gate()` and :meth:`IRNNv2Layer.set_bias_for_gate()` for details on the required input format for ``weights`` and ``bias`` . - namespace INetworkDefinitionDoc - { - constexpr const char* descr = R"trtdoc( - Represents a TensorRT Network from which the Builder can build an Engine + The ``input`` ITensor should contain zero or more index dimensions `{N1, ..., Np}`, followed by two dimensions, defined as follows: - :ivar pooling_output_dimensions_formula: :class:`IOutputDimensionsFormula` The formula from computing the pooling output dimensions. If set to :class:`None` , the default formula is used. The default formula in each dimension is :math:`(inputDim + padding * 2 - kernelSize) / stride + 1` . + | `S_max` is the maximum allowed sequence length (number of RNN iterations) + | `E` specifies the embedding length (unless :const:`RNNInputMode.SKIP` is set, in which case it should match :attr:`IRNNv2Layer.hidden_size` ). - :ivar convolution_output_dimensions_formula: :class:`IOutputDimensionsFormula` **Deprecated** Does not currently work reliably and will be removed in a future release. The formula from computing the convolution output dimensions. If set to :class:`None` , the default formula is used. The default formula in each dimension is :math:`(inputDim + padding * 2 - kernelSize) / stride + 1` . + By default, all sequences in the input are assumed to be size ``max_seq_length`` . To provide explicit sequence lengths for each input sequence in the batch, set :attr:`IRNNv2Layer.seq_lengths` . - :ivar deconvolution_output_dimensions_formula: :class:`IOutputDimensionsFormula` **Deprecated** Does not currently work reliably and will be removed in a future release. The formula from computing the deconvolution output dimensions. If :class:`None` is passed, the default formula is used. The default formula in each dimension is :math:`(inputDim - 1) * stride + kernelSize - 2 * padding` . + The RNN layer outputs up to three tensors. - :ivar num_layers: :class:`int` The number of layers in the network. - :ivar num_inputs: :class:`int` The number of inputs of the network. - :ivar num_outputs: :class:`int` The number of outputs of the network. - :ivar name: :class:`str` The name of the network. This is used so that it can be associated with a built engine. The name must be at most 128 characters in length. TensorRT makes no use of this string except storing it as part of the engine so that it may be retrieved at runtime. A name unique to the builder will be generated by default. - :ivar has_implicit_batch_dimension: :class:`bool` Whether the network was created with an implicit batch dimension. This is a network-wide property. Either all tensors in the network have an implicit batch dimension or none of them do. This is True when the INetworkDefinition is created with default flags: ``create_network()``. To specify explicit batch, set the flag: ``create_network(flags=1 << int(tensorrt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))``. - :ivar has_explicit_precision: :class:`bool` True if and only if this :class:`INetworkDefinition` was created with ``NetworkDefinitionCreationFlag.EXPLICIT_PRECISION`` set: ``create_network(flags=(1 << int(NetworkDefinitionCreationFlag.EXPLICIT_PRECISION)))``. - )trtdoc"; + The first output tensor is the output of the final RNN layer across all timesteps, with dimensions `{N1, ..., Np, S_max, H}`: - constexpr const char* add_input = R"trtdoc( - Adds an input to the network. + | `N1..Np` are the index dimensions specified by the input tensor + | `S_max` is the maximum allowed sequence length (number of RNN iterations) + | `H` is an output hidden state (equal to :attr:`IRNNv2Layer.hidden_size` or 2x :attr:`IRNNv2Layer.hidden_size` ) - :arg name: The name of the tensor. - :arg dtype: The data type of the tensor. Currently, trt.int8 is not supported for inputs. - :arg shape: The dimensions of the tensor. The total volume must be less than 2^30 elements. + The second tensor is the final hidden state of the RNN across all layers, and if the RNN is an LSTM (i.e. :attr:`IRNNv2Layer.op` is :const:`RNNOperation.LSTM` ), then the third tensor is the final cell state of the RNN across all layers. Both the second and third output tensors have dimensions `{N1, ..., Np, L, H}`: - :returns: The newly added Tensor. - )trtdoc"; + | `N1..Np` are the index dimensions specified by the input tensor + | `L` is the number of layers in the RNN, equal to :attr:`IRNNv2Layer.num_layers` + | `H` is the hidden state for each layer, equal to :attr:`IRNNv2Layer.hidden_size` if getDirection is :const:`RNNDirection.UNIDIRECTION`, and 2x :attr:`IRNNv2Layer.hidden_size` otherwise. - constexpr const char* mark_output = R"trtdoc( - Mark a tensor as an output. + :returns: The new RNNv2 layer, or :class:`None` if it could not be created. +)trtdoc"; - :arg tensor: The tensor to mark. - )trtdoc"; +constexpr const char* add_identity = R"trtdoc( + Add an identity layer. + See :class:`IIdentityLayer` for more information. - constexpr const char* add_convolution = R"trtdoc( - Add a 2D convolution layer to the network. - See :class:`IConvolutionLayer` for more information. + :arg input: The input tensor to the layer. - :arg input: The input tensor to the convolution. - :arg num_output_maps: The number of output feature maps for the convolution. - :arg kernel_shape: The dimensions of the convolution kernel. - :arg kernel: The kernel weights for the convolution. - :arg bias: The optional bias weights for the convolution. + :returns: The new identity layer, or :class:`None` if it could not be created. +)trtdoc"; - :returns: The new convolution layer, or :class:`None` if it could not be created. - )trtdoc"; +constexpr const char* add_parametric_relu = R"trtdoc( + Add a parametric ReLU layer. + See :class:`IParametricReLULayer` for more information. - constexpr const char* add_convolution_nd = R"trtdoc( - Add a multi-dimension convolution layer to the network. - See :class:`IConvolutionLayer` for more information. + :arg input: The input tensor to the layer. + :arg slopes: The slopes tensor (input elements are multiplied with the slopes where the input is negative). - :arg input: The input tensor to the convolution. - :arg num_output_maps: The number of output feature maps for the convolution. - :arg kernel_shape: The dimensions of the convolution kernel. - :arg kernel: The kernel weights for the convolution. - :arg bias: The optional bias weights for the convolution. + :returns: The new parametric ReLU layer, or :class:`None` if it could not be created. +)trtdoc"; - :returns: The new convolution layer, or :class:`None` if it could not be created. - )trtdoc"; +constexpr const char* add_resize = R"trtdoc( + Add a resize layer. + See :class:`IResizeLayer` for more information. - constexpr const char* add_fully_connected = R"trtdoc( - Add a fully connected layer to the network. - See :class:`IFullyConnectedLayer` for more information. + :arg input: The input tensor to the layer. - :arg input: The input tensor to the layer. - :arg num_outputs: The number of outputs of the layer. - :arg kernel: The kernel weights for the convolution. - :arg bias: The optional bias weights for the convolution. + :returns: The new resize layer, or :class:`None` if it could not be created. +)trtdoc"; - :returns: The new fully connected layer, or :class:`None` if it could not be created. - )trtdoc"; +constexpr const char* add_loop = R"trtdoc( + Adds a loop to the network, which provides a way to specify a recurrent subgraph. + See :class:`ILoop` for more information. - constexpr const char* add_activation = R"trtdoc( - Add an activation layer to the network. - See :class:`IActivationLayer` for more information. + :returns: The new loop layer, or :class:`None` if it could not be created. +)trtdoc"; - :arg input: The input tensor to the layer. - :arg type: The type of activation function to apply. +constexpr const char* add_shape = R"trtdoc( + Add a shape layer to the network. + See :class:`IShapeLayer` for more information. - :returns: The new activation layer, or :class:`None` if it could not be created. - )trtdoc"; + :arg input: The input tensor to the layer. - constexpr const char* add_pooling = R"trtdoc( - Add a 2D pooling layer to the network. - See :class:`IPoolingLayer` for more information. + :returns: The new shape layer, or :class:`None` if it could not be created. +)trtdoc"; - :arg input: The input tensor to the layer. - :arg type: The type of pooling to apply. - :arg window_size: The size of the pooling window. +constexpr const char* add_select = R"trtdoc( + Add a select layer. + See :class:`ISelectLayer` for more information. - :returns: The new pooling layer, or :class:`None` if it could not be created. - )trtdoc"; + :arg condition: The condition tensor to the layer. + :arg then_input: The then input tensor to the layer. + :arg else_input: The else input tensor to the layer. - constexpr const char* add_pooling_nd = R"trtdoc( - Add a multi-dimension pooling layer to the network. - See :class:`IPoolingLayer` for more information. + :returns: The new select layer, or :class:`None` if it could not be created. +)trtdoc"; - :arg input: The input tensor to the layer. - :arg type: The type of pooling to apply. - :arg window_size: The size of the pooling window. +constexpr const char* add_fill = R"trtdoc( + Add a fill layer. + See :class:`IFillLayer` for more information. - :returns: The new pooling layer, or :class:`None` if it could not be created. - )trtdoc"; + :arg dimensions: The output tensor dimensions. + :arg op: The fill operation that the layer applies. - constexpr const char* add_lrn = R"trtdoc( - Add a LRN layer to the network. - See :class:`ILRNLayer` for more information. + :returns: The new fill layer, or :class:`None` if it could not be created. +)trtdoc"; - :arg input: The input tensor to the layer. - :arg window: The size of the window. - :arg alpha: The alpha value for the LRN computation. - :arg beta: The beta value for the LRN computation. - :arg k: The k value for the LRN computation. +constexpr const char* set_weights_name = R"trtdoc( + Associate a name with all current uses of the given weights. - :returns: The new LRN layer, or :class:`None` if it could not be created. - )trtdoc"; + The name must be set after the Weights are used in the network. + Lookup is associative. The name applies to all Weights with matching + type, value pointer, and count. If Weights with a matching value + pointer, but different type or count exists in the network, an + error message is issued, the name is rejected, and return false. + If the name has already been used for other weights, + return false. None causes the weights to become unnamed, + i.e. clears any previous name. - constexpr const char* add_scale = R"trtdoc( - Add a scale layer to the network. - See :class:`IScaleLayer` for more information. + :arg weights: The weights to be named. + :arg name: The name to associate with the weights. - :arg input: The input tensor to the layer. This tensor is required to have a minimum of 3 dimensions. - :arg mode: The scaling mode. - :arg shift: The shift value. - :arg scale: The scale value. - :arg power: The power value. + :returns: true on success. +)trtdoc"; - If the weights are available, then the size of weights are dependent on the ScaleMode. - For UNIFORM, the number of weights is equal to 1. - For CHANNEL, the number of weights is equal to the channel dimension. - For ELEMENTWISE, the number of weights is equal to the volume of the input. - - :returns: The new scale layer, or :class:`None` if it could not be created. - )trtdoc"; +constexpr const char* remove_tensor = R"trtdoc( + Remove a tensor from the network. - constexpr const char* add_scale_nd = R"trtdoc( - Add a multi-dimension scale layer to the network. - See :class:`IScaleLayer` for more information. + :arg tensor: The tensor to remove - :arg input: The input tensor to the layer. This tensor is required to have a minimum of 3 dimensions. - :arg mode: The scaling mode. - :arg shift: The shift value. - :arg scale: The scale value. - :arg power: The power value. - :arg channel_axis: The channel dimension axis. + It is illegal to remove a tensor that is the input or output of a layer. + if this method is called with such a tensor, a warning will be emitted on the log + and the call will be ignored. +)trtdoc"; - If the weights are available, then the size of weights are dependent on the ScaleMode. - For UNIFORM, the number of weights is equal to 1. - For CHANNEL, the number of weights is equal to the channel dimension. - For ELEMENTWISE, the number of weights is equal to the volume of the input. +constexpr const char* unmark_output = R"trtdoc( + Unmark a tensor as a network output. - :returns: The new scale layer, or :class:`None` if it could not be created. - )trtdoc"; + :arg tensor: The tensor to unmark as an output tensor. +)trtdoc"; - constexpr const char* add_softmax = R"trtdoc( - Add a softmax layer to the network. - See :class:`ISoftMaxLayer` for more information. +constexpr const char* mark_output_for_shapes = R"trtdoc( + Enable tensor's value to be computed by :func:`IExecutionContext.get_shape_binding`. - :arg input: The input tensor to the layer. + :arg tensor: The tensor to unmark as an output tensor. The tensor must be of type :class:`tensorrt.int32` and have no more than one dimension. - :returns: The new softmax layer, or :class:`None` if it could not be created. - )trtdoc"; + :returns: :class:`True` if successful, :class:`False` if tensor is already marked as an output. +)trtdoc"; - constexpr const char* add_concatenation = R"trtdoc( - Add a concatenation layer to the network. Note that all tensors must have the same dimension except for the Channel dimension. - See :class:`IConcatenationLayer` for more information. +constexpr const char* unmark_output_for_shapes = R"trtdoc( + Undo :func:`mark_output_for_shapes` . - :arg inputs: The input tensors to the layer. + :arg tensor: The tensor to unmark as an output tensor. - :returns: The new concatenation layer, or :class:`None` if it could not be created. - )trtdoc"; + :returns: :class:`True` if successful, :class:`False` if tensor is not marked as an output. - constexpr const char* add_deconvolution = R"trtdoc( - Add a 2D deconvolution layer to the network. - See :class:`IDeconvolutionLayer` for more information. +)trtdoc"; - :arg input: The input tensor to the layer. - :arg num_output_maps: The number of output feature maps. - :arg kernel_shape: The dimensions of the convolution kernel. - :arg kernel: The kernel weights for the convolution. - :arg bias: The optional bias weights for the convolution. +constexpr const char* add_plugin_v2 = R"trtdoc( + Add a plugin layer to the network using an :class:`IPluginV2` interface. + See :class:`IPluginV2` for more information. - :returns: The new deconvolution layer, or :class:`None` if it could not be created. - )trtdoc"; + :arg inputs: The input tensors to the layer. + :arg plugin: The layer plugin. - constexpr const char* add_deconvolution_nd = R"trtdoc( - Add a multi-dimension deconvolution layer to the network. - See :class:`IDeconvolutionLayer` for more information. + :returns: The new plugin layer, or :class:`None` if it could not be created. +)trtdoc"; - :arg input: The input tensor to the layer. - :arg num_output_maps: The number of output feature maps. - :arg kernel_shape: The dimensions of the convolution kernel. - :arg kernel: The kernel weights for the convolution. - :arg bias: The optional bias weights for the convolution. +constexpr const char* get_layer = R"trtdoc( + Get the layer specified by the given index. - :returns: The new deconvolution layer, or :class:`None` if it could not be created. - )trtdoc"; + :arg index: The index of the layer. - constexpr const char* add_elementwise = R"trtdoc( - Add an elementwise layer to the network. - See :class:`IElementWiseLayer` for more information. + :returns: The layer, or :class:`None` if it is out of range. +)trtdoc"; - :arg input1: The first input tensor to the layer. - :arg input2: The second input tensor to the layer. - :arg op: The binary operation that the layer applies. +constexpr const char* get_input = R"trtdoc( + Get the input tensor specified by the given index. - The input tensors must have the same number of dimensions. - For each dimension, their lengths must match, or one of them must be one. - In the latter case, the tensor is broadcast along that axis. + :arg index: The index of the input tensor. - The output tensor has the same number of dimensions as the inputs. - For each dimension, its length is the maximum of the lengths of the - corresponding input dimension. + :returns: The tensor, or :class:`None` if it is out of range. +)trtdoc"; - :returns: The new element-wise layer, or :class:`None` if it could not be created. - )trtdoc"; +constexpr const char* get_output = R"trtdoc( + Get the output tensor specified by the given index. - constexpr const char* add_rnn = R"trtdoc( - Add a ``layer_count`` deep RNN layer to the network with a sequence length of ``max_seq_length`` and ``hidden_size`` internal state per layer. - See :class:`IRNNLayer` for more information. + :arg index: The index of the output tensor. - :arg input: The input tensor to the layer. - :arg layer_count: The number of layers in the RNN. - :arg hidden_size: The size of the internal hidden state for each layer. - :arg max_seq_length: The maximum length of the time sequence. - :arg op: The type of RNN to execute. - :arg mode: The input mode for the RNN. - :arg direction: The direction to run the RNN. - :arg weights: The weights for the weight matrix parameters of the RNN. - :arg bias: The weights for the bias vectors parameters of the RNN. + :returns: The tensor, or :class:`None` if it is out of range. +)trtdoc"; - The input tensors must be of the type :const:`float32` or :const:`float16` . +constexpr const char* serialize = R"trtdoc( + Serialize the network to a stream. - See :class:`IRNNLayer` for details on the required input format for ``weights`` and ``bias`` . + :returns: An :class:`IHostMemory` object containing the serialized :class:`INetworkDefinition` . +)trtdoc"; - The layout for the ``input`` tensor should be `{1, S_max, N, E}`, where: +constexpr const char* add_quantize = R"trtdoc( + Add a quantization layer to the network. + See :class:`IQuantizeLayer` for more information. - | `S_max` is the maximum allowed sequence length (number of RNN iterations) - | `N` is the batch size - | `E` specifies the embedding length (unless :const:`RNNInputMode.SKIP` is set, in which case it should match :attr:`hidden_size` ). + :arg input: A tensor to quantize. + :arg scale: A tensor with the scale coefficients. - The first output tensor is the output of the final RNN layer across all timesteps, with dimensions `{S_max, N, H}`: + :returns: The new quantization layer, or :class:`None` if it could not be created. +)trtdoc"; - | `S_max` is the maximum allowed sequence length (number of RNN iterations) - | `N` is the batch size - | `H` is an output hidden state (equal to :attr:`hidden_size` or 2x :attr:`hidden_size` ) +constexpr const char* add_dequantize = R"trtdoc( + Add a dequantization layer to the network. + See :class:`IDequantizeLayer` for more information. - The second tensor is the final hidden state of the RNN across all layers, and if the RNN is an LSTM (i.e. :attr:`op` is :const:`RNNOperation.LSTM` ), then the third tensor is the final cell state of the RNN across all layers. Both the second and third output tensors have dimensions `{L, N, H}`: + :arg input: A tensor to quantize. + :arg scale: A tensor with the scale coefficients. - | `L` is equal to :attr:`num_layers` if getDirection is :const:`RNNDirection.UNIDIRECTION` , and 2* :attr:`num_layers` if getDirection is :const:`RNNDirection.BIDIRECTION` . In the bi-directional case, layer `l`'s final forward hidden state is stored in `L = 2*l`, and final backward hidden state is stored in `L = 2*l + 1` . - | `N` is the batch size - | `H` is :attr:`hidden_size` . + :returns: The new dequantization layer, or :class:`None` if it could not be created. +)trtdoc"; +} // namespace INetworkDefinitionDoc - Note that in bidirectional RNNs, the full "hidden state" for a layer `l` is the concatenation of its forward hidden state and its backward hidden state, and its size is 2*H. - - **Deprecated** IRNNLayer is superseded by IRNNv2Layer. Use add_rnn_v2() instead. - - :returns: The new RNN layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_plugin = R"trtdoc( - Add a plugin layer to the network. - See :class:`IPlugin` for more information. - - :arg inputs: The input tensors to the layer. - :arg plugin: The layer plugin. - - :returns: The new plugin layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_unary = R"trtdoc( - Add a unary layer to the network. - See :class:`IUnaryLayer` for more information. - - :arg input: The input tensor to the layer. - :arg op: The operation to apply. - - :returns: The new unary layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_padding = R"trtdoc( - Add a 2D padding layer to the network. - See :class:`IPaddingLayer` for more information. - - :arg input: The input tensor to the layer. - :arg pre_padding: The padding to apply to the start of the tensor. - :arg post_padding: The padding to apply to the end of the tensor. - - :returns: The new padding layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_padding_nd = R"trtdoc( - Add a multi-dimensional padding layer to the network. - See :class:`IPaddingLayer` for more information. - - :arg input: The input tensor to the layer. - :arg pre_padding: The padding to apply to the start of the tensor. - :arg post_padding: The padding to apply to the end of the tensor. - - :returns: The new padding layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_shuffle = R"trtdoc( - Add a shuffle layer to the network. - See :class:`IShuffleLayer` for more information. - - :arg input: The input tensor to the layer. - - :returns: The new shuffle layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_slice = R"trtdoc( - Add a slice layer to the network. - See :class:`ISliceLayer` for more information. - - :arg input: The input tensor to the layer. - :arg start: The start offset. - :arg shape: The output shape. - :arg stride: The slicing stride. Positive, negative, zero stride values, and combinations of them in different dimensions are allowed. - - :returns: The new slice layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_reduce = R"trtdoc( - Add a reduce layer to the network. - See :class:`IReduceLayer` for more information. - - :arg input: The input tensor to the layer. - :arg op: The reduction operation to perform. - :arg axes: The reduction dimensions. - - | Bit 0 of the uint32_t type corresponds to the non-batch dimension 0 boolean and so on. - | If a bit is set, then the corresponding dimension will be reduced. - | Let's say we have an NCHW tensor as input (three non-batch dimensions). - | Bit 0 corresponds to the C dimension boolean. - | Bit 1 corresponds to the H dimension boolean. - | Bit 2 corresponds to the W dimension boolean. - | Note that reduction is not permitted over the batch size dimension. - :arg keep_dims: The boolean that specifies whether or not to keep the reduced dimensions in the output of the layer. - - :returns: The new reduce layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_topk = R"trtdoc( - Add a TopK layer to the network. - See :class:`ITopKLayer` for more information. - - The TopK layer has two outputs of the same dimensions. The first contains data values, the second contains index positions for the values. Output values are sorted, largest first for operation :const:`TopKOperation.MAX` and smallest first for operation :const:`TopKOperation.MIN` . - - Currently only values of K up to 1024 are supported. - - :arg input: The input tensor to the layer. - :arg op: Operation to perform. - :arg k: Number of elements to keep. - - :arg axes: The reduction dimensions. - Bit 0 of the uint32_t type corresponds to the non-batch dimension 0 boolean and so on. - If a bit is set, then the corresponding dimension will be reduced. - Let's say we have an NCHW tensor as input (three non-batch dimensions). - Bit 0 corresponds to the C dimension boolean. - Bit 1 corresponds to the H dimension boolean. - Bit 2 corresponds to the W dimension boolean. - Note that TopK reduction is currently only permitted over one dimension. - - :returns: The new TopK layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_gather = R"trtdoc( - Add a pooling layer to the network. - See :class:`IGatherLayer` for more information. - - :arg input: The tensor to gather values from. - :arg indices: The tensor to get indices from to populate the output tensor. - :arg axis: The non-batch dimension axis in the data tensor to gather on. - - :returns: The new pooling layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_ragged_softmax = R"trtdoc( - Add a ragged softmax layer to the network. - See :class:`IRaggedSoftMaxLayer` for more information. - - :arg input: The ZxS input tensor. - :arg bounds: The Zx1 bounds tensor. - - :returns: The new ragged softmax layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_matrix_multiply = R"trtdoc( - Add a matrix multiply layer to the network. - See :class:`IMatrixMultiplyLayer` for more information. - - :arg input0: The first input tensor (commonly A). - :arg op0: Whether to treat input0 as matrices, transposed matrices, or vectors. - :arg input1: The second input tensor (commonly B). - :arg op1: Whether to treat input1 as matrices, transposed matrices, or vectors. - - :returns: The new matrix multiply layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_matrix_multiply_deprecated = R"trtdoc( - Add a matrix multiply layer to the network. - See :class:`IMatrixMultiplyLayer` for more information. - - :arg input0: The first input tensor (commonly A). - :arg transpose0: If true, op(input0)=transpose(input0), else op(input0)=input0. - :arg input1: The second input tensor (commonly B). - :arg transpose1: If true, op(input1)=transpose(input1), else op(input1)=input1. - - :returns: The new matrix multiply layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_constant = R"trtdoc( - Add a constant layer to the network. - See :class:`IConstantLayer` for more information. - - :arg shape: The shape of the constant. - :arg weights: The constant value, represented as weights. - - :returns: The new constant layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_rnn_v2 = R"trtdoc( - Add an RNNv2 layer to the network. - See :class:`IRNNv2Layer` for more information. - - Add an ``layer_count`` deep RNN layer to the network with ``hidden_size`` internal states that can take a batch with fixed or variable sequence lengths. - - :arg input: The input tensor to the layer (see below). - :arg layer_count: The number of layers in the RNN. - :arg hidden_size: Size of the internal hidden state for each layer. - :arg max_seq_length: Maximum sequence length for the input. - :arg op: The type of RNN to execute. - - By default, the layer is configured with :const:`RNNDirection.UNIDIRECTION` and :const:`RNNInputMode.LINEAR` . To change these settings, set :attr:`IRNNv2Layer.direction` and :attr:`IRNNv2Layer.input_mode` . - - Weights and biases for the added layer should be set using :meth:`IRNNv2Layer.set_weights_for_gate()` and :meth:`IRNNv2Layer.set_bias_for_gate()` prior to building an engine using this network. - - The input tensors must be of the type :const:`float32` or :const:`float16` . - The layout of the weights is row major and must be the same datatype as the input tensor. - ``weights`` contain 8 matrices and ``bias`` contains 8 vectors. - - See :meth:`IRNNv2Layer.set_weights_for_gate()` and :meth:`IRNNv2Layer.set_bias_for_gate()` for details on the required input format for ``weights`` and ``bias`` . - - The ``input`` ITensor should contain zero or more index dimensions `{N1, ..., Np}`, followed by two dimensions, defined as follows: - - | `S_max` is the maximum allowed sequence length (number of RNN iterations) - | `E` specifies the embedding length (unless :const:`RNNInputMode.SKIP` is set, in which case it should match :attr:`IRNNv2Layer.hidden_size` ). - - By default, all sequences in the input are assumed to be size ``max_seq_length`` . To provide explicit sequence lengths for each input sequence in the batch, set :attr:`IRNNv2Layer.seq_lengths` . - - The RNN layer outputs up to three tensors. - - The first output tensor is the output of the final RNN layer across all timesteps, with dimensions `{N1, ..., Np, S_max, H}`: - - | `N1..Np` are the index dimensions specified by the input tensor - | `S_max` is the maximum allowed sequence length (number of RNN iterations) - | `H` is an output hidden state (equal to :attr:`IRNNv2Layer.hidden_size` or 2x :attr:`IRNNv2Layer.hidden_size` ) - - The second tensor is the final hidden state of the RNN across all layers, and if the RNN is an LSTM (i.e. :attr:`IRNNv2Layer.op` is :const:`RNNOperation.LSTM` ), then the third tensor is the final cell state of the RNN across all layers. Both the second and third output tensors have dimensions `{N1, ..., Np, L, H}`: - - | `N1..Np` are the index dimensions specified by the input tensor - | `L` is the number of layers in the RNN, equal to :attr:`IRNNv2Layer.num_layers` - | `H` is the hidden state for each layer, equal to :attr:`IRNNv2Layer.hidden_size` if getDirection is :const:`RNNDirection.UNIDIRECTION`, and 2x :attr:`IRNNv2Layer.hidden_size` otherwise. - - :returns: The new RNNv2 layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_plugin_ext = R"trtdoc( - Add a plugin layer to the network using an :class:`IPluginExt` interface. - See :class:`IPluginExt` for more information. - - :arg inputs: The input tensors to the layer. - :arg plugin: The layer plugin. - - :returns: The new plugin layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_identity = R"trtdoc( - Add an identity layer. - See :class:`IIdentityLayer` for more information. - - :arg input: The input tensor to the layer. - - :returns: The new identity layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_parametric_relu = R"trtdoc( - Add a parametric ReLU layer. - See :class:`IParametricReLULayer` for more information. - - :arg input: The input tensor to the layer. - :arg slopes: The slopes tensor (input elements are multiplied with the slopes where the input is negative). - - :returns: The new parametric ReLU layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_resize = R"trtdoc( - Add a resize layer. - See :class:`IResizeLayer` for more information. - - :arg input: The input tensor to the layer. - - :returns: The new resize layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_loop = R"trtdoc( - Adds a loop to the network, whcih provides a way to specify a recurrent subgraph. - See :class:`ILoop` for more information. - - :returns: The new loop layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_shape = R"trtdoc( - Add a shape layer to the network. - See :class:`IShapeLayer` for more information. - - :arg input: The input tensor to the layer. - - :returns: The new shape layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_select = R"trtdoc( - Add a select layer. - See :class:`ISelectLayer` for more information. - - :arg condition: The condition tensor to the layer. - :arg then_input: The then input tensor to the layer. - :arg else_input: The else input tensor to the layer. - - :returns: The new select layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* add_fill = R"trtdoc( - Add a fill layer. - See :class:`IFillLayer` for more information. - - :arg dimensions: The output tensor dimensions. - :arg op: The fill operation that the layer applies. - - :returns: The new fill layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* remove_tensor = R"trtdoc( - Remove a tensor from the network. - - :arg tensor: The tensor to remove - - It is illegal to remove a tensor that is the input or output of a layer. - if this method is called with such a tensor, a warning will be emitted on the log - and the call will be ignored. - )trtdoc"; - - constexpr const char* unmark_output = R"trtdoc( - Unmark a tensor as a network output. - - :arg tensor: The tensor to unmark as an output tensor. - )trtdoc"; - - constexpr const char* mark_output_for_shapes = R"trtdoc( - Enable tensor's value to be computed by :func:`IExecutionContext.get_shape_binding`. - - :arg tensor: The tensor to unmark as an output tensor. The tensor must be of type :class:`tensorrt.int32` and have no more than one dimension. - - :returns: :class:`True` if successful, :class:`False` if tensor is already marked as an output. - )trtdoc"; - - constexpr const char* unmark_output_for_shapes = R"trtdoc( - Undo :func:`mark_output_for_shapes` . - - :arg tensor: The tensor to unmark as an output tensor. - - :returns: :class:`True` if successful, :class:`False` if tensor is not marked as an output. - - )trtdoc"; - - constexpr const char* add_plugin_v2 = R"trtdoc( - Add a plugin layer to the network using an :class:`IPluginV2` interface. - See :class:`IPluginV2` for more information. - - :arg inputs: The input tensors to the layer. - :arg plugin: The layer plugin. - - :returns: The new plugin layer, or :class:`None` if it could not be created. - )trtdoc"; - - constexpr const char* get_layer = R"trtdoc( - Get the layer specified by the given index. - - :arg index: The index of the layer. - - :returns: The layer, or :class:`None` if it is out of range. - )trtdoc"; - - constexpr const char* get_input = R"trtdoc( - Get the input tensor specified by the given index. - - :arg index: The index of the input tensor. - - :returns: The tensor, or :class:`None` if it is out of range. - )trtdoc"; - - constexpr const char* get_output = R"trtdoc( - Get the output tensor specified by the given index. - - :arg index: The index of the output tensor. - - :returns: The tensor, or :class:`None` if it is out of range. - )trtdoc"; - - constexpr const char* serialize = R"trtdoc( - Serialize the network to a stream. - - :returns: An :class:`IHostMemory` object containing the serialized :class:`INetworkDefinition` . - )trtdoc"; - - } // INetworkDefinitionDoc - -} // tensorrt +} // namespace tensorrt diff --git a/python/docstrings/infer/pyInt8Doc.h b/python/docstrings/infer/pyInt8Doc.h index 6c707714..57fb9c50 100644 --- a/python/docstrings/infer/pyInt8Doc.h +++ b/python/docstrings/infer/pyInt8Doc.h @@ -14,176 +14,177 @@ * limitations under the License. */ -// This file contains all int8 calibration related docstrings, since these are typically too long to keep in the binding code. +// This file contains all int8 calibration related docstrings, since these are typically too long to keep in the binding +// code. #pragma once namespace tensorrt { - namespace CalibrationAlgoTypeDoc - { - constexpr const char* descr = R"trtdoc( - Version of calibration algorithm to use. - )trtdoc"; - } /* CalibrationAlgoTypeDoc */ +namespace CalibrationAlgoTypeDoc +{ +constexpr const char* descr = R"trtdoc( + Version of calibration algorithm to use. +)trtdoc"; +} // namespace CalibrationAlgoTypeDoc - namespace IInt8CalibratorDoc - { - constexpr const char* descr = R"trtdoc( - Application-implemented interface for calibration. Calibration is a step performed by the builder when deciding suitable scale factors for 8-bit inference. It must also provide a method for retrieving representative images which the calibration process can use to examine the distribution of activations. It may optionally implement a method for caching the calibration result for reuse on subsequent runs. +namespace IInt8CalibratorDoc +{ +constexpr const char* descr = R"trtdoc( + Application-implemented interface for calibration. Calibration is a step performed by the builder when deciding suitable scale factors for 8-bit inference. It must also provide a method for retrieving representative images which the calibration process can use to examine the distribution of activations. It may optionally implement a method for caching the calibration result for reuse on subsequent runs. - :ivar batch_size: :class:`int` The batch size used for calibration batches. - :ivar algorithm: :class:`CalibrationAlgoType` The algorithm used by this calibrator. - )trtdoc"; + :ivar batch_size: :class:`int` The batch size used for calibration batches. + :ivar algorithm: :class:`CalibrationAlgoType` The algorithm used by this calibrator. +)trtdoc"; - constexpr const char* get_batch_size = R"trtdoc( - Get the batch size used for calibration batches. +constexpr const char* get_batch_size = R"trtdoc( + Get the batch size used for calibration batches. - :returns: The batch size. - )trtdoc"; + :returns: The batch size. +)trtdoc"; - constexpr const char* get_algorithm = R"trtdoc( - Get the algorithm used by this calibrator. +constexpr const char* get_algorithm = R"trtdoc( + Get the algorithm used by this calibrator. - :returns: The algorithm used by this calibrator. - )trtdoc"; + :returns: The algorithm used by this calibrator. +)trtdoc"; - constexpr const char* get_batch = R"trtdoc( - Get a batch of input for calibration. The batch size of the input must match the batch size returned by :func:`get_batch_size` . +constexpr const char* get_batch = R"trtdoc( + Get a batch of input for calibration. The batch size of the input must match the batch size returned by :func:`get_batch_size` . - A possible implementation may look like this: - :: + A possible implementation may look like this: + :: - def get_batch(names): - try: - # Assume self.batches is a generator that provides batch data. - data = next(self.batches) - # Assume that self.device_input is a device buffer allocated by the constructor. - cuda.memcpy_htod(self.device_input, data) - return [int(self.device_input)] - except StopIteration: - # When we're out of batches, we return either [] or None. - # This signals to TensorRT that there is no calibration data remaining. - return None + def get_batch(names): + try: + # Assume self.batches is a generator that provides batch data. + data = next(self.batches) + # Assume that self.device_input is a device buffer allocated by the constructor. + cuda.memcpy_htod(self.device_input, data) + return [int(self.device_input)] + except StopIteration: + # When we're out of batches, we return either [] or None. + # This signals to TensorRT that there is no calibration data remaining. + return None - :arg names: The names of the network inputs for each object in the bindings array. + :arg names: The names of the network inputs for each object in the bindings array. - :returns: A :class:`list` of device memory pointers set to the memory containing each network input data, or an empty :class:`list` if there are no more batches for calibration. You can allocate these device buffers with pycuda, for example, and then cast them to :class:`int` to retrieve the pointer. - )trtdoc"; + :returns: A :class:`list` of device memory pointers set to the memory containing each network input data, or an empty :class:`list` if there are no more batches for calibration. You can allocate these device buffers with pycuda, for example, and then cast them to :class:`int` to retrieve the pointer. +)trtdoc"; - constexpr const char* read_calibration_cache = R"trtdoc( - Load a calibration cache. +constexpr const char* read_calibration_cache = R"trtdoc( + Load a calibration cache. - Calibration is potentially expensive, so it can be useful to generate the calibration data once, then use it on subsequent builds - of the network. The cache includes the regression cutoff and quantile values used to generate it, and will not be used if - these do not match the settings of the current calibrator. However, the network should also be recalibrated if its structure - changes, or the input data set changes, and it is the responsibility of the application to ensure this. + Calibration is potentially expensive, so it can be useful to generate the calibration data once, then use it on subsequent builds + of the network. The cache includes the regression cutoff and quantile values used to generate it, and will not be used if + these do not match the settings of the current calibrator. However, the network should also be recalibrated if its structure + changes, or the input data set changes, and it is the responsibility of the application to ensure this. - Reading a cache is just like reading any other file in Python. For example, one possible implementation is: - :: + Reading a cache is just like reading any other file in Python. For example, one possible implementation is: + :: - def read_calibration_cache(self): - # If there is a cache, use it instead of calibrating again. Otherwise, implicitly return None. - if os.path.exists(self.cache_file): - with open(self.cache_file, "rb") as f: - return f.read() + def read_calibration_cache(self): + # If there is a cache, use it instead of calibrating again. Otherwise, implicitly return None. + if os.path.exists(self.cache_file): + with open(self.cache_file, "rb") as f: + return f.read() - :returns: A cache object or None if there is no data. - )trtdoc"; + :returns: A cache object or None if there is no data. +)trtdoc"; - constexpr const char* write_calibration_cache = R"trtdoc( - Save a calibration cache. +constexpr const char* write_calibration_cache = R"trtdoc( + Save a calibration cache. - Writing a cache is just like writing any other buffer in Python. For example, one possible implementation is: - :: + Writing a cache is just like writing any other buffer in Python. For example, one possible implementation is: + :: - def write_calibration_cache(self, cache): - with open(self.cache_file, "wb") as f: - f.write(cache) + def write_calibration_cache(self, cache): + with open(self.cache_file, "wb") as f: + f.write(cache) - :arg cache: The calibration cache to write. - )trtdoc"; + :arg cache: The calibration cache to write. +)trtdoc"; - } /* IInt8CalibratorDoc */ +} // namespace IInt8CalibratorDoc - namespace IInt8LegacyCalibratorDoc - { - constexpr const char* descr = R"trtdoc( - Extends the :class:`IInt8Calibrator` class. - This calibrator requires user parameterization, and is provided as a fallback option if the other calibrators yield poor results. - - :ivar quantile: :class:`float` The quantile (between 0 and 1) that will be used to select the region maximum when the quantile method is in use. See the user guide for more details on how the quantile is used. - :ivar regression_cutoff: :class:`float` The fraction (between 0 and 1) of the maximum used to define the regression cutoff when using regression to determine the region maximum. See the user guide for more details on how the regression cutoff is used - )trtdoc"; +namespace IInt8LegacyCalibratorDoc +{ +constexpr const char* descr = R"trtdoc( + Extends the :class:`IInt8Calibrator` class. + This calibrator requires user parameterization, and is provided as a fallback option if the other calibrators yield poor results. - constexpr const char* readHistogramCache = R"trtdoc( - Load a histogram. - Histogram generation is potentially expensive, so it can be useful to generate the histograms once, then use them when exploring - the space of calibrations. The histograms should be regenerated if the network structure - changes, or the input data set changes, and it is the responsibility of the application to ensure this. - See the user guide for more details on how the regression cutoff is used + :ivar quantile: :class:`float` The quantile (between 0 and 1) that will be used to select the region maximum when the quantile method is in use. See the user guide for more details on how the quantile is used. + :ivar regression_cutoff: :class:`float` The fraction (between 0 and 1) of the maximum used to define the regression cutoff when using regression to determine the region maximum. See the user guide for more details on how the regression cutoff is used +)trtdoc"; - :arg length: The length of the cached data, that should be set by the called function. If there is no data, this should be zero. +constexpr const char* readHistogramCache = R"trtdoc( + Load a histogram. + Histogram generation is potentially expensive, so it can be useful to generate the histograms once, then use them when exploring + the space of calibrations. The histograms should be regenerated if the network structure + changes, or the input data set changes, and it is the responsibility of the application to ensure this. + See the user guide for more details on how the regression cutoff is used - :returns: The cache or None if there is no cache. - )trtdoc"; + :arg length: The length of the cached data, that should be set by the called function. If there is no data, this should be zero. - constexpr const char* writeHistogramCache = R"trtdoc( - Save a histogram cache. + :returns: The cache or None if there is no cache. +)trtdoc"; - :arg data: The data to cache. - :arg length: The length in bytes of the data to cache. - )trtdoc"; +constexpr const char* writeHistogramCache = R"trtdoc( + Save a histogram cache. - constexpr const char* get_algorithm = R"trtdoc( - Signals that this is the legacy calibrator. + :arg data: The data to cache. + :arg length: The length in bytes of the data to cache. +)trtdoc"; - :returns: :class:`CalibrationAlgoType.LEGACY_CALIBRATION` - )trtdoc"; - } /* IInt8LegacyCalibratorDoc */ +constexpr const char* get_algorithm = R"trtdoc( + Signals that this is the legacy calibrator. - namespace IInt8EntropyCalibratorDoc - { - constexpr const char* descr = R"trtdoc( - Extends the :class:`IInt8Calibrator` class. + :returns: :class:`CalibrationAlgoType.LEGACY_CALIBRATION` +)trtdoc"; +} // namespace IInt8LegacyCalibratorDoc - This is the Legacy Entropy calibrator. It is less complicated than the legacy calibrator and produces better results. - )trtdoc"; +namespace IInt8EntropyCalibratorDoc +{ +constexpr const char* descr = R"trtdoc( + Extends the :class:`IInt8Calibrator` class. - constexpr const char* get_algorithm = R"trtdoc( - Signals that this is the entropy calibrator. + This is the Legacy Entropy calibrator. It is less complicated than the legacy calibrator and produces better results. +)trtdoc"; - :returns: :class:`CalibrationAlgoType.ENTROPY_CALIBRATION` - )trtdoc"; - } /* IInt8EntropyCalibratorDoc */ +constexpr const char* get_algorithm = R"trtdoc( + Signals that this is the entropy calibrator. - namespace IInt8EntropyCalibrator2Doc - { - constexpr const char* descr = R"trtdoc( - Extends the :class:`IInt8Calibrator` class. + :returns: :class:`CalibrationAlgoType.ENTROPY_CALIBRATION` +)trtdoc"; +} // namespace IInt8EntropyCalibratorDoc - This is the preferred calibrator. This is the required calibrator for DLA, as it supports per activation tensor scaling. - )trtdoc"; +namespace IInt8EntropyCalibrator2Doc +{ +constexpr const char* descr = R"trtdoc( + Extends the :class:`IInt8Calibrator` class. - constexpr const char* get_algorithm = R"trtdoc( - Signals that this is the entropy calibrator 2. + This is the preferred calibrator. This is the required calibrator for DLA, as it supports per activation tensor scaling. +)trtdoc"; - :returns: :class:`CalibrationAlgoType.ENTROPY_CALIBRATION_2` - )trtdoc"; - } /* IInt8EntropyCalibrator2Doc */ +constexpr const char* get_algorithm = R"trtdoc( + Signals that this is the entropy calibrator 2. - namespace IInt8MinMaxCalibratorDoc - { - constexpr const char* descr = R"trtdoc( - Extends the :class:`IInt8Calibrator` class. + :returns: :class:`CalibrationAlgoType.ENTROPY_CALIBRATION_2` +)trtdoc"; +} // namespace IInt8EntropyCalibrator2Doc - This is the preferred calibrator for NLP tasks for all backends. It supports per activation tensor scaling. - )trtdoc"; +namespace IInt8MinMaxCalibratorDoc +{ +constexpr const char* descr = R"trtdoc( + Extends the :class:`IInt8Calibrator` class. - constexpr const char* get_algorithm = R"trtdoc( - Signals that this is the minmax calibrator. + This is the preferred calibrator for NLP tasks for all backends. It supports per activation tensor scaling. +)trtdoc"; - :returns: :class:`CalibrationAlgoType.MINMAX_CALIBRATION` - )trtdoc"; - } /* IInt8MinMaxCalibratorDoc */ +constexpr const char* get_algorithm = R"trtdoc( + Signals that this is the minmax calibrator. -} /* tensorrt */ + :returns: :class:`CalibrationAlgoType.MINMAX_CALIBRATION` +)trtdoc"; +} // namespace IInt8MinMaxCalibratorDoc + +} // namespace tensorrt diff --git a/python/docstrings/infer/pyPluginDoc.h b/python/docstrings/infer/pyPluginDoc.h index 46f33f2f..9a43ca63 100644 --- a/python/docstrings/infer/pyPluginDoc.h +++ b/python/docstrings/infer/pyPluginDoc.h @@ -18,401 +18,284 @@ namespace tensorrt { - namespace IPluginDoc - { - constexpr const char* descr = R"trtdoc( - Plugin class for user-implemented layers. - Plugins are a mechanism for applications to implement custom layers. Each plugin is owned by the application, and its lifetime must span any use of it by TensorRT. +namespace IPluginV2Doc +{ +constexpr const char* descr = R"trtdoc( + Plugin class for user-implemented layers. - :ivar num_outputs: :class:`int` The number of outputs from the layer. This is used by the implementations of :class:`INetworkDefinition` and :class:`Builder` . In particular, it is called prior to any call to :func:`initialize` . - :ivar serialization_size: :class:`int` The size of the serialization buffer required. - )trtdoc"; + Plugins are a mechanism for applications to implement custom layers. When + combined with IPluginCreator it provides a mechanism to register plugins and + look up the Plugin Registry during de-serialization. - constexpr const char* get_output_shape = R"trtdoc( - Get the dimension of an output tensor. - :arg index: The index of the output tensor. - :arg input_shapes: The shapes of the input tensors. + :ivar num_outputs: :class:`int` The number of outputs from the layer. This is used by the implementations of :class:`INetworkDefinition` and :class:`Builder` . In particular, it is called prior to any call to :func:`initialize` . + :ivar tensorrt_version: :class:`int` The API version with which this plugin was built. + :ivar plugin_type: :class:`str` The plugin type. Should match the plugin name returned by the corresponding plugin creator + :ivar plugin_version: :class:`str` The plugin version. Should match the plugin version returned by the corresponding plugin creator. + :ivar plugin_namespace: :class:`str` The namespace that this plugin object belongs to. Ideally, all plugin objects from the same plugin library should have the same namespace. + :ivar serialization_size: :class:`int` The size of the serialization buffer required. +)trtdoc"; - This function is called by the implementations of :class:`INetworkDefinition` and :class:`Builder` . In particular, it is called prior to any call to :func:`initialize` . - )trtdoc"; +constexpr const char* get_output_shape = R"trtdoc( + Get the dimension of an output tensor. - constexpr const char* configure = R"trtdoc( - Configure the layer. + :arg index: The index of the output tensor. + :arg input_shapes: The shapes of the input tensors. - This function is called by the :class:`Builder` prior to :func:`initialize` . It provides an opportunity for the layer to make algorithm choices on the basis of its weights, dimensions, and maximum batch size. The type is assumed to be FP32 and format NCHW. + This function is called by the implementations of :class:`INetworkDefinition` and :class:`Builder` . In particular, it is called prior to any call to :func:`initialize` . +)trtdoc"; - :arg input_shapes: The shapes of the input tensors. - :arg output_shapes: The shapes of the output tensors. - :arg max_batch_size: The maximum batch size. +constexpr const char* supports_format = R"trtdoc( + Check format support. - The shapes passed here do not include the outermost batch size (i.e. for 2D image networks, they will be 3D CHW dimensions). + This function is called by the implementations of :class:`INetworkDefinition` , :class:`Builder` , and :class:`ICudaEngine` . In particular, it is called when creating an engine and when deserializing an engine. - This method is not called for :class:`IPluginExt` classes; :func:`configure_with_format` is called instead. - )trtdoc"; + :arg dtype: Data type requested. + :arg format: TensorFormat requested. - constexpr const char* initialize = R"trtdoc( - Initialize the layer for execution. This is called when the engine is created. + :returns: True if the plugin supports the type-format combination. +)trtdoc"; - :returns: 0 for success, else non-zero (which will cause engine termination). - )trtdoc"; +constexpr const char* configure_with_format = R"trtdoc( + Configure the layer. - constexpr const char* terminate = R"trtdoc( - Release resources acquired during plugin layer initialization. This is called when the engine is destroyed. - )trtdoc"; + This function is called by the :class:`Builder` prior to :func:`initialize` . It provides an opportunity for the layer to make algorithm choices on the basis of its weights, dimensions, and maximum batch size. - constexpr const char* get_workspace_size = R"trtdoc( - Find the workspace size required by the layer. + The dimensions passed here do not include the outermost batch size (i.e. for 2D image networks, they will be 3D CHW dimensions). - This function is called during engine startup, after :func:`initialize` . The workspace size returned should be sufficient for any batch size up to the maximum. + :arg input_shapes: The shapes of the input tensors. + :arg output_shapes: The shapes of the output tensors. + :arg dtype: The data type selected for the engine. + :arg format: The format selected for the engine. + :arg max_batch_size: The maximum batch size. +)trtdoc"; - :arg max_batch_size: :class:`int` The maximum possible batch size during inference. +constexpr const char* initialize = R"trtdoc( + Initialize the layer for execution. This is called when the engine is created. - :returns: The workspace size. - )trtdoc"; + :returns: 0 for success, else non-zero (which will cause engine termination). +)trtdoc"; - constexpr const char* execute_async = R"trtdoc( - Execute the layer asynchronously. +constexpr const char* terminate = R"trtdoc( + Release resources acquired during plugin layer initialization. This is called when the engine is destroyed. +)trtdoc"; - :arg batch_size: The number of inputs in the batch. - :arg inputs: The memory for the input tensors. - :arg outputs: The memory for the output tensors. - :arg workspace: Workspace for execution. - :arg stream_handle: The stream in which to execute the kernels. +constexpr const char* get_workspace_size = R"trtdoc( + Find the workspace size required by the layer. - :returns: 0 for success, else non-zero (which will cause engine termination). - )trtdoc"; + This function is called during engine startup, after :func:`initialize` . The workspace size returned should be sufficient for any batch size up to the maximum. - constexpr const char* serialize = R"trtdoc( - Serialize the layer. + :arg max_batch_size: :class:`int` The maximum possible batch size during inference. - :arg buffer: A buffer of size at least :attr:`serialization_size` . - )trtdoc"; + :returns: The workspace size. +)trtdoc"; - } /* IPluginDoc */ +constexpr const char* execute_async = R"trtdoc( + Execute the layer asynchronously. - namespace IPluginExtDoc - { - constexpr const char* descr = R"trtdoc( - Plugin class for user-implemented layers. + :arg batch_size: The number of inputs in the batch. + :arg inputs: The memory for the input tensors. + :arg outputs: The memory for the output tensors. + :arg workspace: Workspace for execution. + :arg stream_handle: The stream in which to execute the kernels. - Plugins are a mechanism for applications to implement custom layers. Each plugin is owned by the application, and its lifetime must span any use of it by TensorRT. + :returns: 0 for success, else non-zero (which will cause engine termination). +)trtdoc"; - :ivar tensorrt_version: :class:`int` The API version with which this plugin was built. - )trtdoc"; +constexpr const char* serialize = R"trtdoc( + Serialize the plugin. +)trtdoc"; - constexpr const char* supports_format = R"trtdoc( - Check format support. +constexpr const char* destroy = R"trtdoc( + Destroy the plugin object. This will be called when the :class:`INetworkDefinition` , :class:`Builder` or :class:`ICudaEngine` is destroyed. +)trtdoc"; - This function is called by the implementations of :class:`INetworkDefinition` , :class:`Builder` , and :class:`ICudaEngine` . In particular, it is called when creating an engine and when deserializing an engine. +constexpr const char* clone = R"trtdoc( + Clone the plugin object. This copies over internal plugin parameters and returns a new plugin object with these parameters. +)trtdoc"; +} // namespace IPluginV2Doc - :arg dtype: Data type requested. - :arg format: TensorFormat requested. +namespace IPluginV2ExtDoc +{ +constexpr const char* descr = R"trtdoc( + Plugin class for user-implemented layers. - :returns: True if the plugin supports the type-format combination. - )trtdoc"; + Plugins are a mechanism for applications to implement custom layers. This interface provides additional capabilities to the IPluginV2 interface by supporting different output data types. - constexpr const char* configure_with_format = R"trtdoc( - Configure the layer. + :ivar tensorrt_version: :class:`int` The API version with which this plugin was built. +)trtdoc"; - This function is called by the :class:`Builder` prior to :func:`initialize` . It provides an opportunity for the layer to make algorithm choices on the basis of its weights, dimensions, and maximum batch size. +constexpr const char* get_output_data_type = R"trtdoc( - The dimensions passed here do not include the outermost batch size (i.e. for 2D image networks, they will be 3D CHW dimensions). + Return the DataType of the plugin output at the requested index. + The default behavior should be to return the type of the first input, or DataType::kFLOAT if the layer has no inputs. + The returned data type must have a format that is supported by the plugin. - :arg input_shapes: The shapes of the input tensors. - :arg output_shapes: The shapes of the output tensors. - :arg dtype: The data type selected for the engine. - :arg format: The format selected for the engine. - :arg max_batch_size: The maximum batch size. - )trtdoc"; + :arg index: Index of the output for which Data type is requested. + :arg input_types: Data types of the inputs. - } /* IPluginExtDoc */ + :returns: DataType of the plugin output at the requested index. +)trtdoc"; - namespace IPluginV2Doc - { - constexpr const char* descr = R"trtdoc( - Plugin class for user-implemented layers. +constexpr const char* configure_plugin = R"trtdoc( + Configure the layer. - Plugins are a mechanism for applications to implement custom layers. When - combined with IPluginCreator it provides a mechanism to register plugins and - look up the Plugin Registry during de-serialization. + This function is called by the :class:`Builder` prior to :func:`initialize` . It provides an opportunity for the layer to make algorithm choices on the basis of its weights, dimensions, and maximum batch size. + The dimensions passed here do not include the outermost batch size (i.e. for 2D image networks, they will be 3D CHW dimensions). - :ivar num_outputs: :class:`int` The number of outputs from the layer. This is used by the implementations of :class:`INetworkDefinition` and :class:`Builder` . In particular, it is called prior to any call to :func:`initialize` . - :ivar tensorrt_version: :class:`int` The API version with which this plugin was built. - :ivar plugin_type: :class:`str` The plugin type. Should match the plugin name returned by the corresponding plugin creator - :ivar plugin_version: :class:`str` The plugin version. Should match the plugin version returned by the corresponding plugin creator. - :ivar plugin_namespace: :class:`str` The namespace that this plugin object belongs to. Ideally, all plugin objects from the same plugin library should have the same namespace. - :ivar serialization_size: :class:`int` The size of the serialization buffer required. - )trtdoc"; + :arg input_shapes: The shapes of the input tensors. + :arg output_shapes: The shapes of the output tensors. + :arg input_types: The data types of the input tensors. + :arg output_types: The data types of the output tensors. + :arg input_is_broadcasted: Whether an input is broadcasted across the batch. + :arg output_is_broadcasted: Whether an output is broadcasted across the batch. + :arg format: The format selected for floating-point inputs and outputs of the engine. + :arg max_batch_size: The maximum batch size. +)trtdoc"; - constexpr const char* get_output_shape = R"trtdoc( - Get the dimension of an output tensor. +constexpr const char* clone = R"trtdoc( + Clone the plugin object. This copies over internal plugin parameters as well and returns a new plugin object with these parameters. - :arg index: The index of the output tensor. - :arg input_shapes: The shapes of the input tensors. + If the source plugin is pre-configured with configure_plugin(), the returned object should also be pre-configured. The returned object should allow attach_to_context() with a new execution context. + Cloned plugin objects can share the same per-engine immutable resource (e.g. weights) with the source object (e.g. via ref-counting) to avoid duplication. +)trtdoc"; - This function is called by the implementations of :class:`INetworkDefinition` and :class:`Builder` . In particular, it is called prior to any call to :func:`initialize` . - )trtdoc"; +constexpr const char* attach_to_context = R"trtdoc( + Attach the plugin object to an execution context and grant the plugin the access to some context resource. - constexpr const char* supports_format = R"trtdoc( - Check format support. + :arg cudnn The cudnn context handle of the execution context + :arg cublas The cublas context handle of the execution context + :arg allocator The allocator used by the execution context - This function is called by the implementations of :class:`INetworkDefinition` , :class:`Builder` , and :class:`ICudaEngine` . In particular, it is called when creating an engine and when deserializing an engine. + This function is called automatically for each plugin when a new execution context is created. If the plugin needs per-context resource, it can be allocated here. The plugin can also get context-owned CUDNN and CUBLAS context here. +)trtdoc"; - :arg dtype: Data type requested. - :arg format: TensorFormat requested. +constexpr const char* detach_from_context = R"trtdoc( + Detach the plugin object from its execution context. - :returns: True if the plugin supports the type-format combination. - )trtdoc"; + This function is called automatically for each plugin when a execution context is destroyed. If the plugin owns per-context resource, it can be released here. +)trtdoc"; +} // namespace IPluginV2ExtDoc - constexpr const char* configure_with_format = R"trtdoc( - Configure the layer. +namespace PluginFieldTypeDoc +{ +constexpr const char* descr = R"trtdoc( + The possible field types for custom layer. +)trtdoc"; +} // namespace PluginFieldTypeDoc - This function is called by the :class:`Builder` prior to :func:`initialize` . It provides an opportunity for the layer to make algorithm choices on the basis of its weights, dimensions, and maximum batch size. +namespace PluginFieldDoc +{ +constexpr const char* descr = R"trtdoc( + Contains plugin attribute field names and associated data. + This information can be parsed to decode necessary plugin metadata - The dimensions passed here do not include the outermost batch size (i.e. for 2D image networks, they will be 3D CHW dimensions). + :ivar name: :class:`str` Plugin field attribute name. + :ivar data: :class:`buffer` Plugin field attribute data. + :ivar type: :class:`PluginFieldType` Plugin field attribute type. + :ivar size: :class:`int` Number of data entries in the Plugin attribute. +)trtdoc"; +} // namespace PluginFieldDoc - :arg input_shapes: The shapes of the input tensors. - :arg output_shapes: The shapes of the output tensors. - :arg dtype: The data type selected for the engine. - :arg format: The format selected for the engine. - :arg max_batch_size: The maximum batch size. - )trtdoc"; +namespace PluginFieldCollectionDoc +{ +constexpr const char* descr = R"trtdoc( + Contains plugin attribute field names and associated data. + This information can be parsed to decode necessary plugin metadata - constexpr const char* initialize = R"trtdoc( - Initialize the layer for execution. This is called when the engine is created. + :ivar num_fields: :class:`int` Number of :class:`PluginField` entries. + :ivar fields: :class:`list` PluginField entries. +)trtdoc"; +} // namespace PluginFieldCollectionDoc - :returns: 0 for success, else non-zero (which will cause engine termination). - )trtdoc"; +namespace IPluginCreatorDoc +{ +constexpr const char* descr = R"trtdoc( + Plugin creator class for user implemented layers - constexpr const char* terminate = R"trtdoc( - Release resources acquired during plugin layer initialization. This is called when the engine is destroyed. - )trtdoc"; + :ivar tensorrt_version: :class:`int` Number of :class:`PluginField` entries. + :ivar name: :class:`str` Plugin name. + :ivar plugin_version: :class:`str` Plugin version. + :ivar field_names: :class:`list` List of fields that needs to be passed to :func:`create_plugin` . + :ivar plugin_namespace: :class:`str` The namespace of the plugin creator based on the plugin library it belongs to. This can be set while registering the plugin creator. +)trtdoc"; - constexpr const char* get_workspace_size = R"trtdoc( - Find the workspace size required by the layer. +constexpr const char* create_plugin = R"trtdoc( + Creates a new plugin. - This function is called during engine startup, after :func:`initialize` . The workspace size returned should be sufficient for any batch size up to the maximum. + :arg name: The name of the plugin. + :arg field_collection: The :class:`PluginFieldCollection` for this plugin. - :arg max_batch_size: :class:`int` The maximum possible batch size during inference. + :returns: :class:`IPluginV2` or :class:`None` on failure. +)trtdoc"; - :returns: The workspace size. - )trtdoc"; +constexpr const char* deserialize_plugin = R"trtdoc( + Creates a plugin object from a serialized plugin. - constexpr const char* execute_async = R"trtdoc( - Execute the layer asynchronously. + :arg name: Name of the plugin. + :arg serialized_plugin: A buffer containing a serialized plugin. + + :returns: A new :class:`IPluginV2` +)trtdoc"; +} // namespace IPluginCreatorDoc + +namespace IPluginRegistryDoc +{ +constexpr const char* descr = R"trtdoc( + Registers plugin creators. + + :ivar plugin_creator_list: All the registered plugin creators. + :ivar error_recorder: :class:`IErrorRecorder` Application-implemented error reporting interface for TensorRT objects. +)trtdoc"; + +constexpr const char* register_creator = R"trtdoc( + Register a plugin creator. + + :arg creator: The IPluginCreator instance. + :arg plugin_namespace: The namespace of the plugin creator. + + :returns: False if one with the same type is already registered. +)trtdoc"; + +constexpr const char* deregister_creator = R"trtdoc( + Deregister a previously registered plugin creator. - :arg batch_size: The number of inputs in the batch. - :arg inputs: The memory for the input tensors. - :arg outputs: The memory for the output tensors. - :arg workspace: Workspace for execution. - :arg stream_handle: The stream in which to execute the kernels. + Since there may be a desire to limit the number of plugins, + this function provides a mechanism for removing plugin creators registered in TensorRT. + The plugin creator that is specified by ``creator`` is removed from TensorRT and no longer tracked. + + :arg creator: The IPluginCreator instance. + + :returns: ``True`` if the plugin creator was deregistered, ``False`` if it was not found in the registry + or otherwise could not be deregistered. +)trtdoc"; + +constexpr const char* get_plugin_creator = R"trtdoc( + Return plugin creator based on type and version + + :arg type: The type of the plugin. + :arg version: The version of the plugin. + :arg plugin_namespace: The namespace of the plugin. + + :returns: An :class:`IPluginCreator` . +)trtdoc"; +} // namespace IPluginRegistryDoc + +namespace FreeFunctionsDoc +{ +constexpr const char* get_plugin_registry = R"trtdoc( + Return the plugin registry +)trtdoc"; + +constexpr const char* init_libnvinfer_plugins = R"trtdoc( + Initialize and register all the existing TensorRT plugins to the :class:`IPluginRegistry` with an optional namespace. + The plugin library author should ensure that this function name is unique to the library. + This function should be called once before accessing the Plugin Registry. - :returns: 0 for success, else non-zero (which will cause engine termination). - )trtdoc"; + :arg logger: Logger to print plugin registration information. + :arg namespace: Namespace used to register all the plugins in this library. +)trtdoc"; +} // namespace FreeFunctionsDoc - constexpr const char* serialize = R"trtdoc( - Serialize the plugin. - )trtdoc"; - - constexpr const char* destroy = R"trtdoc( - Destroy the plugin object. This will be called when the :class:`INetworkDefinition` , :class:`Builder` or :class:`ICudaEngine` is destroyed. - )trtdoc"; - - constexpr const char* clone = R"trtdoc( - Clone the plugin object. This copies over internal plugin parameters and returns a new plugin object with these parameters. - )trtdoc"; - } /* IPluginV2Doc */ - - namespace IPluginV2ExtDoc - { - constexpr const char* descr = R"trtdoc( - Plugin class for user-implemented layers. - - Plugins are a mechanism for applications to implement custom layers. This interface provides additional capabilities to the IPluginV2 interface by supporting different output data types. - - :ivar tensorrt_version: :class:`int` The API version with which this plugin was built. - )trtdoc"; - - constexpr const char* get_output_data_type = R"trtdoc( - - Return the DataType of the plugin output at the requested index. - The default behavior should be to return the type of the first input, or DataType::kFLOAT if the layer has no inputs. - The returned data type must have a format that is supported by the plugin. - - :arg index: Index of the output for which Data type is requested. - :arg input_types: Data types of the inputs. - - :returns: DataType of the plugin output at the requested index. - )trtdoc"; - - constexpr const char* configure_plugin = R"trtdoc( - Configure the layer. - - This function is called by the :class:`Builder` prior to :func:`initialize` . It provides an opportunity for the layer to make algorithm choices on the basis of its weights, dimensions, and maximum batch size. - - The dimensions passed here do not include the outermost batch size (i.e. for 2D image networks, they will be 3D CHW dimensions). - - :arg input_shapes: The shapes of the input tensors. - :arg output_shapes: The shapes of the output tensors. - :arg input_types: The data types of the input tensors. - :arg output_types: The data types of the output tensors. - :arg input_is_broadcasted: Whether an input is broadcasted across the batch. - :arg output_is_broadcasted: Whether an output is broadcasted across the batch. - :arg format: The format selected for floating-point inputs and outputs of the engine. - :arg max_batch_size: The maximum batch size. - )trtdoc"; - - constexpr const char* clone = R"trtdoc( - Clone the plugin object. This copies over internal plugin parameters as well and returns a new plugin object with these parameters. - - If the source plugin is pre-configured with configure_plugin(), the returned object should also be pre-configured. The returned object should allow attach_to_context() with a new execution context. - Cloned plugin objects can share the same per-engine immutable resource (e.g. weights) with the source object (e.g. via ref-counting) to avoid duplication. - )trtdoc"; - - constexpr const char* attach_to_context = R"trtdoc( - Attach the plugin object to an execution context and grant the plugin the access to some context resource. - - :arg cudnn The cudnn context handle of the execution context - :arg cublas The cublas context handle of the execution context - :arg allocator The allocator used by the execution context - - This function is called automatically for each plugin when a new execution context is created. If the plugin needs per-context resource, it can be allocated here. The plugin can also get context-owned CUDNN and CUBLAS context here. - )trtdoc"; - - constexpr const char* detach_from_context = R"trtdoc( - Detach the plugin object from its execution context. - - This function is called automatically for each plugin when a execution context is destroyed. If the plugin owns per-context resource, it can be released here. - )trtdoc"; - } /* IPluginExtDoc */ - - - namespace PluginFieldTypeDoc - { - constexpr const char* descr = R"trtdoc( - The possible field types for custom layer. - )trtdoc"; - } /* PluginFieldTypeDoc */ - - namespace PluginFieldDoc - { - constexpr const char* descr = R"trtdoc( - Contains plugin attribute field names and associated data. - This information can be parsed to decode necessary plugin metadata - - :ivar name: :class:`str` Plugin field attribute name. - :ivar data: :class:`buffer` Plugin field attribute data. - :ivar type: :class:`PluginFieldType` Plugin field attribute type. - :ivar size: :class:`int` Number of data entries in the Plugin attribute. - )trtdoc"; - } /* PluginFieldDoc */ - - namespace PluginFieldCollectionDoc - { - constexpr const char* descr = R"trtdoc( - Contains plugin attribute field names and associated data. - This information can be parsed to decode necessary plugin metadata - - :ivar num_fields: :class:`int` Number of :class:`PluginField` entries. - :ivar fields: :class:`list` PluginField entries. - )trtdoc"; - } /* PluginFieldCollectionDoc */ - - namespace IPluginCreatorDoc - { - constexpr const char* descr = R"trtdoc( - Plugin creator class for user implemented layers - - :ivar tensorrt_version: :class:`int` Number of :class:`PluginField` entries. - :ivar name: :class:`str` Plugin name. - :ivar plugin_version: :class:`str` Plugin version. - :ivar field_names: :class:`list` List of fields that needs to be passed to :func:`create_plugin` . - :ivar plugin_namespace: :class:`str` The namespace of the plugin creator based on the plugin library it belongs to. This can be set while registering the plugin creator. - )trtdoc"; - - constexpr const char* create_plugin = R"trtdoc( - Creates a new plugin. - - :arg name: The name of the plugin. - :arg field_collection: The :class:`PluginFieldCollection` for this plugin. - - :returns: :class:`IPluginV2` or :class:`None` on failure. - )trtdoc"; - - constexpr const char* deserialize_plugin = R"trtdoc( - Creates a plugin object from a serialized plugin. - - :arg name: Name of the plugin. - :arg serialized_plugin: A buffer containing a serialized plugin. - - :returns: A new :class:`IPluginV2` - )trtdoc"; - } /* IPluginCreatorDoc */ - - - namespace IPluginRegistryDoc - { - constexpr const char* descr = R"trtdoc( - Registers plugin creators. - - :ivar plugin_creator_list: All the registered plugin creators. - )trtdoc"; - - constexpr const char* register_creator = R"trtdoc( - Register a plugin creator. - - :arg creator: The IPluginCreator instance. - :arg plugin_namespace: The namespace of the plugin creator. - - :returns: False if one with the same type is already registered. - )trtdoc"; - - constexpr const char* get_plugin_creator = R"trtdoc( - Return plugin creator based on type and version - - :arg type: The type of the plugin. - :arg version: The version of the plugin. - :arg plugin_namespace: The namespace of the plugin. - - :returns: An :class:`IPluginCreator` . - )trtdoc"; - } /* IPluginRegistryDoc */ - - namespace FreeFunctionsDoc - { - constexpr const char* get_plugin_registry = R"trtdoc( - Return the plugin registry - )trtdoc"; - - - constexpr const char* init_libnvinfer_plugins = R"trtdoc( - Initialize and register all the existing TensorRT plugins to the :class:`IPluginRegistry` with an optional namespace. - The plugin library author should ensure that this function name is unique to the library. - This function should be called once before accessing the Plugin Registry. - - :arg logger: Logger to print plugin registration information. - :arg namespace: Namespace used to register all the plugins in this library. - )trtdoc"; - } /* FreeFunctionsDoc */ - - namespace IPluginFactoryDoc - { - constexpr const char* descr = R"trtdoc( - Plugin factory for deserialization - )trtdoc"; - - constexpr const char* create_plugin = R"trtdoc( - Create a plugin from serialized data. - - Responsibility of destroying this plugin lies with the application. It can be done anytime after consumers of this plugin are destroyed. - - :arg layer_name: The name of the layer. - :arg serialized_plugin: The serialized plugin. - - :returns: The plugin. - )trtdoc"; - } /* IPluginFactoryDoc */ - -} /* tensorrt */ +} // namespace tensorrt diff --git a/python/docstrings/parsers/pyCaffeDoc.h b/python/docstrings/parsers/pyCaffeDoc.h index 80489da7..eb299476 100644 --- a/python/docstrings/parsers/pyCaffeDoc.h +++ b/python/docstrings/parsers/pyCaffeDoc.h @@ -19,137 +19,92 @@ namespace tensorrt { - namespace ICaffeParserDoc - { - constexpr const char* descr = R"trtdoc( - This class is used for parsing Caffe models. It allows users to export models trained using Caffe to TRT. +namespace ICaffeParserDoc +{ +constexpr const char* descr = R"trtdoc( + This class is used for parsing Caffe models. It allows users to export models trained using Caffe to TRT. - :ivar plugin_factory: :class:`ICaffePluginFactory` The ICaffePluginFactory used to create the user defined plugins. - :ivar plugin_factory_ext: :class:`ICaffePluginFactoryExt` The ICaffePluginFactoryExt used to create the user defined pluginExts. - :ivar plugin_factory_v2: :class:`ICaffePluginFactoryV2` The ICaffePluginFactory used to create the user defined plugins. - :ivar plugin_namespace: :class:`str` The namespace used to lookup and create plugins in the network. - :ivar protobuf_buffer_size: :class:`int` The buffer size for the parsing and storage of the learned model. - )trtdoc"; + :ivar plugin_factory_v2: :class:`ICaffePluginFactoryV2` The ICaffePluginFactory used to create the user defined plugins. + :ivar plugin_namespace: :class:`str` The namespace used to lookup and create plugins in the network. + :ivar protobuf_buffer_size: :class:`int` The buffer size for the parsing and storage of the learned model. + :ivar error_recorder: :class:`IErrorRecorder` Application-implemented error reporting interface for TensorRT objects. +)trtdoc"; - constexpr const char* parse = R"trtdoc( - Parse a prototxt file and a binaryproto Caffe model to extract network definition and weights associated with the network, respectively. +constexpr const char* parse = R"trtdoc( + Parse a prototxt file and a binaryproto Caffe model to extract network definition and weights associated with the network, respectively. - :arg deploy: The plain text, prototxt file used to define the network definition. - :arg model: The binaryproto Caffe model that contains the weights associated with the network. - :arg network: Network in which the CaffeParser will fill the layers. - :arg dtype: The type to which the weights will be transformed. + :arg deploy: The plain text, prototxt file used to define the network definition. + :arg model: The binaryproto Caffe model that contains the weights associated with the network. + :arg network: Network in which the CaffeParser will fill the layers. + :arg dtype: The type to which the weights will be transformed. - :returns: An :class:`IBlobNameToTensor` object that contains the extracted data. - )trtdoc"; + :returns: An :class:`IBlobNameToTensor` object that contains the extracted data. +)trtdoc"; - constexpr const char* parse_buffer = R"trtdoc( - Parse a prototxt file and a binaryproto Caffe model to extract network definition and weights associated with the network, respectively. +constexpr const char* parse_buffer = R"trtdoc( + Parse a prototxt file and a binaryproto Caffe model to extract network definition and weights associated with the network, respectively. - :arg deploy_buffer: The memory buffer containing the plain text deploy prototxt used to define the network definition. - :arg model_buffer: The binaryproto Caffe memory buffer that contains the weights associated with the network. - :arg network: Network in which the CaffeParser will fill the layers. - :arg dtype: The type to which the weights will be transformed. + :arg deploy_buffer: The memory buffer containing the plain text deploy prototxt used to define the network definition. + :arg model_buffer: The binaryproto Caffe memory buffer that contains the weights associated with the network. + :arg network: Network in which the CaffeParser will fill the layers. + :arg dtype: The type to which the weights will be transformed. - :returns: An :class:`IBlobNameToTensor` object that contains the extracted data. - )trtdoc"; + :returns: An :class:`IBlobNameToTensor` object that contains the extracted data. +)trtdoc"; - constexpr const char* parse_binary_proto = R"trtdoc( - Parse and extract data stored in binaryproto file. The binaryproto file contains data stored in a binary blob. :func:`parse_binary_proto` converts it to an :class:`numpy.ndarray` object. +constexpr const char* parse_binary_proto = R"trtdoc( + Parse and extract data stored in binaryproto file. The binaryproto file contains data stored in a binary blob. :func:`parse_binary_proto` converts it to an :class:`numpy.ndarray` object. - :arg filename: Path to file containing binary proto. + :arg filename: Path to file containing binary proto. - :returns: :class:`numpy.ndarray` An array that contains the extracted data. - )trtdoc"; + :returns: :class:`numpy.ndarray` An array that contains the extracted data. +)trtdoc"; - } /* ICaffeParserDoc */ +} // namespace ICaffeParserDoc - namespace IBlobNameToTensorDoc - { - constexpr const char* descr = R"trtdoc( - This class is used to store and query :class:`ITensor` s after they have been extracted from a Caffe model using the :class:`CaffeParser` . - )trtdoc"; +namespace IBlobNameToTensorDoc +{ +constexpr const char* descr = R"trtdoc( + This class is used to store and query :class:`ITensor` s after they have been extracted from a Caffe model using the :class:`CaffeParser` . +)trtdoc"; - constexpr const char* find = R"trtdoc( - Given a blob name, this function returns an :class:`ITensor` object. +constexpr const char* find = R"trtdoc( + Given a blob name, this function returns an :class:`ITensor` object. - :arg name: Caffe blob name for which the user wants the corresponding :class:`ITensor` . + :arg name: Caffe blob name for which the user wants the corresponding :class:`ITensor` . - :returns: A :class:`ITensor` object corresponding to the queried name. If no such :class:`ITensor` exists, then an empty object is returned. - )trtdoc"; - } /* IBlobNameToTensorDoc */ + :returns: A :class:`ITensor` object corresponding to the queried name. If no such :class:`ITensor` exists, then an empty object is returned. +)trtdoc"; +} // namespace IBlobNameToTensorDoc - namespace ICaffePluginFactoryDoc - { - constexpr const char* descr = R"trtdoc( - Plugin factory used to configure plugins. - )trtdoc"; +namespace ICaffePluginFactoryV2Doc +{ +constexpr const char* descr = R"trtdoc( + Plugin factory used to configure plugins. +)trtdoc"; - constexpr const char* is_plugin = R"trtdoc( - A user implemented function that determines if a layer configuration is provided by an :class:`IPlugin` . +constexpr const char* is_plugin_v2 = R"trtdoc( + A user implemented function that determines if a layer configuration is provided by an :class:`IPluginV2` . - :arg layer_name: Name of the layer which the user wishes to validate. + :arg layer_name: Name of the layer which the user wishes to validate. - :returns: True if the the layer configuration is provided by an :class:`IPlugin` . - )trtdoc"; + :returns: True if the the layer configuration is provided by an :class:`IPluginV2` . +)trtdoc"; - constexpr const char* create_plugin = R"trtdoc( - Creates a plugin. +constexpr const char* create_plugin = R"trtdoc( + Creates a plugin. - :arg layer_name: Name of layer associated with the plugin. - :arg weights: Weights used for the layer. + :arg layer_name: Name of layer associated with the plugin. + :arg weights: Weights used for the layer. - :returns: The newly created :class:`IPlugin` . - )trtdoc"; - } //IPluginFactoryExtDoc + :returns: The newly created :class:`IPluginV2` . +)trtdoc"; +} // namespace ICaffePluginFactoryV2Doc - namespace ICaffePluginFactoryExtDoc - { - constexpr const char* descr = R"trtdoc( - Plugin factory used to configure plugins with added support for TRT versioning. - )trtdoc"; - - constexpr const char* is_plugin_ext = R"trtdoc( - A user implemented function that determines if a layer configuration is provided by an :class:`IPluginExt` . - - :arg layer_name: Name of the layer which the user wishes to validate. - - :returns: True if the the layer configuration is provided by an :class:`IPluginExt` . - )trtdoc"; - - constexpr const char* get_version = R"trtdoc( - Get the Tensorrt Version. - )trtdoc"; - } //IPluginFactoryExtDoc - - namespace ICaffePluginFactoryV2Doc - { - constexpr const char* descr = R"trtdoc( - Plugin factory used to configure plugins. - )trtdoc"; - - constexpr const char* is_plugin_v2 = R"trtdoc( - A user implemented function that determines if a layer configuration is provided by an :class:`IPluginV2` . - - :arg layer_name: Name of the layer which the user wishes to validate. - - :returns: True if the the layer configuration is provided by an :class:`IPluginV2` . - )trtdoc"; - - constexpr const char* create_plugin = R"trtdoc( - Creates a plugin. - - :arg layer_name: Name of layer associated with the plugin. - :arg weights: Weights used for the layer. - - :returns: The newly created :class:`IPluginV2` . - )trtdoc"; - } //IPluginFactoryV2Doc - - - namespace FreeFunctionsDoc - { - constexpr const char* shutdown_protobuf_library = R"trtdoc( - Shuts down protocol buffers library. - )trtdoc"; - } -} /* tensorrt */ +namespace FreeFunctionsDoc +{ +constexpr const char* shutdown_protobuf_library = R"trtdoc( + Shuts down protocol buffers library. +)trtdoc"; +} +} // namespace tensorrt diff --git a/python/docstrings/parsers/pyOnnxDoc.h b/python/docstrings/parsers/pyOnnxDoc.h index 0e8dead2..89b98825 100644 --- a/python/docstrings/parsers/pyOnnxDoc.h +++ b/python/docstrings/parsers/pyOnnxDoc.h @@ -14,127 +14,132 @@ * limitations under the License. */ +// Docstrings for the pyCaffe parser bindings. #pragma once namespace tensorrt { namespace OnnxParserDoc { - constexpr const char* descr = R"trtdoc( - This class is used for parsing ONNX models into a TensorRT network definition +constexpr const char* descr = R"trtdoc( + This class is used for parsing ONNX models into a TensorRT network definition - :ivar num_errors: :class:`int` The number of errors that occurred during prior calls to :func:`parse` - )trtdoc"; + :ivar num_errors: :class:`int` The number of errors that occurred during prior calls to :func:`parse` +)trtdoc"; - constexpr const char* init = R"trtdoc( - :arg network: The network definition to which the parser will write. - :arg logger: The logger to use. - )trtdoc"; +constexpr const char* init = R"trtdoc( + :arg network: The network definition to which the parser will write. + :arg logger: The logger to use. +)trtdoc"; - constexpr const char* parse = R"trtdoc( - Parse a serialized ONNX model into the TensorRT network. +constexpr const char* parse = R"trtdoc( + Parse a serialized ONNX model into the TensorRT network. - :arg model: The serialized ONNX model. - :arg path: The path to the model file. Only required if the model has externally stored weights. + :arg model: The serialized ONNX model. + :arg path: The path to the model file. Only required if the model has externally stored weights. - :returns: true if the model was parsed successfully - )trtdoc"; + :returns: true if the model was parsed successfully +)trtdoc"; - constexpr const char* parseFromFile = R"trtdoc( - Parse an ONNX model from file into a TensorRT network. +constexpr const char* parse_with_weight_descriptors = R"trtdoc( + Parse a serialized ONNX model into the TensorRT network with consideration of user provided weights. - :arg model: The path to an ONNX model. + :arg model: The serialized ONNX model. - :returns: true if the model was parsed successfully - )trtdoc"; + :returns: true if the model was parsed successfully +)trtdoc"; - constexpr const char* supports_model = R"trtdoc( - Check whether TensorRT supports a particular ONNX model. +constexpr const char* parse_from_file = R"trtdoc( + Parse an ONNX model from file into a TensorRT network. - :arg model: The serialized ONNX model. - :arg path: The path to the model file. Only required if the model has externally stored weights. + :arg model: The path to an ONNX model. - :returns: Tuple[bool, List[Tuple[NodeIndices, bool]]] - The first element of the tuple indicates whether the model is supported. - The second indicates subgraphs (by node index) in the model and whether they are supported. - )trtdoc"; + :returns: true if the model was parsed successfully +)trtdoc"; - constexpr const char* supports_operator = R"trtdoc( - Returns whether the specified operator may be supported by the parser. - Note that a result of true does not guarantee that the operator will be supported in all cases. A more accurate report can be generated by supports_model(). +constexpr const char* supports_model = R"trtdoc( + Check whether TensorRT supports a particular ONNX model. - :arg op_name: The name of the ONNX operator to check for support - )trtdoc"; + :arg model: The serialized ONNX model. + :arg path: The path to the model file. Only required if the model has externally stored weights. - constexpr const char* get_error = R"trtdoc( - Get an error that occurred during prior calls to :func:`parse` + :returns: Tuple[bool, List[Tuple[NodeIndices, bool]]] + The first element of the tuple indicates whether the model is supported. + The second indicates subgraphs (by node index) in the model and whether they are supported. +)trtdoc"; - :arg index: Index of the error - )trtdoc"; +constexpr const char* supports_operator = R"trtdoc( + Returns whether the specified operator may be supported by the parser. + Note that a result of true does not guarantee that the operator will be supported in all cases (i.e., this function may return false-positives). - constexpr const char* clear_errors = R"trtdoc( - Clear errors from prior calls to :func:`parse` - )trtdoc"; + :arg op_name: The name of the ONNX operator to check for support +)trtdoc"; - constexpr const char* get_refit_map = R"trtdoc( - Get description of all weights that could be refit. - :returns: The names of ONNX weights that can be refitted, along with their corresponding TensorRT layer and weight role. - )trtdoc"; - } /* OnnxParserDoc */ +constexpr const char* get_error = R"trtdoc( + Get an error that occurred during prior calls to :func:`parse` - namespace ErrorCodeDoc - { - constexpr const char* descr = R"trtdoc( - The type of parser error - )trtdoc"; - } /* ErrorCodeDoc */ + :arg index: Index of the error +)trtdoc"; - namespace ParserErrorDoc - { - constexpr const char* descr = R"trtdoc( - An object containing information about an error - )trtdoc"; +constexpr const char* clear_errors = R"trtdoc( + Clear errors from prior calls to :func:`parse` +)trtdoc"; - constexpr const char* code = R"trtdoc( - :returns: The error code - )trtdoc"; +} // namespace OnnxParserDoc - constexpr const char* desc = R"trtdoc( - :returns: Description of the error - )trtdoc"; +namespace ErrorCodeDoc +{ +constexpr const char* descr = R"trtdoc( + The type of parser error +)trtdoc"; +} // namespace ErrorCodeDoc - constexpr const char* file = R"trtdoc( - :returns: Source file in which the error occurred - )trtdoc"; +namespace ParserErrorDoc +{ +constexpr const char* descr = R"trtdoc( + An object containing information about an error +)trtdoc"; - constexpr const char* line = R"trtdoc( - :returns: Source line at which the error occurred - )trtdoc"; +constexpr const char* code = R"trtdoc( + :returns: The error code +)trtdoc"; - constexpr const char* func = R"trtdoc( - :returns: Source function in which the error occurred - )trtdoc"; +constexpr const char* desc = R"trtdoc( + :returns: Description of the error +)trtdoc"; - constexpr const char* node = R"trtdoc( - :returns: Index of the Onnx model node in which the error occurred - )trtdoc"; - } /* IParserErrorDoc */ +constexpr const char* file = R"trtdoc( + :returns: Source file in which the error occurred +)trtdoc"; - constexpr const char* get_nv_onnx_parser_version = R"trtdoc( - :returns: The Onnx version - )trtdoc"; +constexpr const char* line = R"trtdoc( + :returns: Source line at which the error occurred +)trtdoc"; - namespace IOnnxPluginFactoryDoc - { - constexpr const char* descr = R"trtdoc( - This plugin factory handles deserialization of the plugins that are built - into the ONNX parser. Engines with legacy plugin layers built using the ONNX parser - must use this plugin factory during deserialization. - )trtdoc"; +constexpr const char* func = R"trtdoc( + :returns: Source function in which the error occurred +)trtdoc"; - constexpr const char* init = R"trtdoc( - :arg logger: The logger to use. - )trtdoc"; - } /* IOnnxPluginFactoryDoc */ +constexpr const char* node = R"trtdoc( + :returns: Index of the Onnx model node in which the error occurred +)trtdoc"; +} // namespace ParserErrorDoc -} /* tensorrt */ +constexpr const char* get_nv_onnx_parser_version = R"trtdoc( +:returns: The Onnx version +)trtdoc"; + +namespace IOnnxPluginFactoryDoc +{ +constexpr const char* descr = R"trtdoc( + This plugin factory handles deserialization of the plugins that are built + into the ONNX parser. Engines with legacy plugin layers built using the ONNX parser + must use this plugin factory during deserialization. +)trtdoc"; + +constexpr const char* init = R"trtdoc( + :arg logger: The logger to use. +)trtdoc"; +} // namespace IOnnxPluginFactoryDoc + +} // namespace tensorrt diff --git a/python/docstrings/parsers/pyUffDoc.h b/python/docstrings/parsers/pyUffDoc.h index 5a98d21b..344f83bf 100644 --- a/python/docstrings/parsers/pyUffDoc.h +++ b/python/docstrings/parsers/pyUffDoc.h @@ -18,141 +18,96 @@ namespace tensorrt { - namespace UffInputOrderDoc - { - constexpr const char* descr = R"trtdoc( - The different possible supported input orders. - )trtdoc"; +namespace UffInputOrderDoc +{ +constexpr const char* descr = R"trtdoc( + The different possible supported input orders. +)trtdoc"; - } /* UffInputOrder */ +} // namespace UffInputOrderDoc - namespace FieldTypeDoc - { - constexpr const char* descr = R"trtdoc( - The possible field types for the custom layer. - )trtdoc"; +namespace FieldTypeDoc +{ +constexpr const char* descr = R"trtdoc( + The possible field types for the custom layer. +)trtdoc"; - } /* FieldType */ +} // namespace FieldTypeDoc - namespace FieldMapDoc - { - constexpr const char* descr = R"trtdoc( - This is a class containing an array of field params used as a layer parameter for plugin layers. The node fields are passed by the parser to the API through the plugin constructor. The implementation of the plugin should parse the contents of the :class:`FieldMap` as part of the plugin constructor. +namespace FieldMapDoc +{ +constexpr const char* descr = R"trtdoc( + This is a class containing an array of field params used as a layer parameter for plugin layers. The node fields are passed by the parser to the API through the plugin constructor. The implementation of the plugin should parse the contents of the :class:`FieldMap` as part of the plugin constructor. - :ivar name: :class:`str` field param - :ivar data: :class:`capsule` field param - :ivar type: :class:`FieldType` field param - :ivar length: :class:`int` field param - )trtdoc"; + :ivar name: :class:`str` field param + :ivar data: :class:`capsule` field param + :ivar type: :class:`FieldType` field param + :ivar length: :class:`int` field param +)trtdoc"; - } /* FieldMap */ +} // namespace FieldMapDoc - namespace FieldCollectionDoc - { - constexpr const char* descr = R"trtdoc( - This class contains an array of :class:`FieldMap` s. +namespace FieldCollectionDoc +{ +constexpr const char* descr = R"trtdoc( + This class contains an array of :class:`FieldMap` s. - :ivar num_fields: :class:`int` The number of :class:`FieldMap` s. - :ivar fields: :class:`capsule` The array of :class:`FieldMap` s. - )trtdoc"; + :ivar num_fields: :class:`int` The number of :class:`FieldMap` s. + :ivar fields: :class:`capsule` The array of :class:`FieldMap` s. +)trtdoc"; - } /* FieldCollection */ +} // namespace FieldCollectionDoc - namespace IUffPluginFactoryDoc - { - constexpr const char* descr = R"trtdoc( - Plugin factory used to configure plugins. - )trtdoc"; +namespace UffParserDoc +{ - constexpr const char* is_plugin = R"trtdoc( - A user implemented function that determines if a layer configuration is provided by an :class:`IPlugin` . +constexpr const char* descr = R"trtdoc( + This class is used for parsing models described using the UFF format. - :arg layer_name: Name of the layer which the user wishes to validate. + :ivar uff_required_version_major: :class:`int` Version Major of the UFF. + :ivar uff_required_version_minor: :class:`int` Version Minor of the UFF. + :ivar uff_required_version_patch: :class:`int` Version Patch of the UFF. + :ivar plugin_namespace: :class:`str` The namespace used to lookup and create plugins in the network. + :ivar error_recorder: :class:`IErrorRecorder` Application-implemented error reporting interface for TensorRT objects. +)trtdoc"; - :returns: True if the the layer configuration is provided by an :class:`IPlugin` . - )trtdoc"; +constexpr const char* register_input = R"trtdoc( + Register an input name of a UFF network with the associated Dimensions. - constexpr const char* create_plugin = R"trtdoc( - Creates a plugin. + :arg name: Input name. + :arg shape: Input shape. + :arg order: Input order on which the framework input was originally. - :arg layer_name: Name of layer associated with the plugin. - :arg weights: Weights used for the layer. - :arg field_collection: A collection of FieldMaps used as layer parameters for different plugin layers. + :returns: True if the name registers without error. +)trtdoc"; - :returns: The newly created :class:`IPlugin` . - )trtdoc"; - } //IPluginFactoryExtDoc +constexpr const char* register_output = R"trtdoc( + Register an output name of a UFF network. - namespace IUffPluginFactoryExtDoc - { - constexpr const char* descr = R"trtdoc( - Plugin factory used to configure plugins with added support for TRT versioning. - )trtdoc"; + :arg output_name: Output name. - constexpr const char* is_plugin_ext = R"trtdoc( - A user implemented function that determines if a layer configuration is provided by an :class:`IPluginExt` . + :returns: True if the name registers without error. +)trtdoc"; - :arg layer_name: Name of the layer which the user wishes to validate. +constexpr const char* parse = R"trtdoc( + Parse a UFF file. - :returns: True if the the layer configuration is provided by an :class:`IPluginExt` . - )trtdoc"; + :arg file: File name of the UFF file. + :arg network: Network in which the :class:`UffParser` will fill the layers. + :arg weights_type: The type on which the weights will be transformed in. - constexpr const char* get_version = R"trtdoc( - Get the Tensorrt Version - )trtdoc"; - } //IPluginFactoryExtDoc + :returns: True if the UFF file is parsed without error. +)trtdoc"; - namespace UffParserDoc - { +constexpr const char* parse_buffer = R"trtdoc( + Parse a UFF buffer - useful if the file is already live in memory. - constexpr const char* descr = R"trtdoc( - This class is used for parsing models described using the UFF format. + :arg buffer: The UFF buffer. + :arg network: Network in which the UFFParser will fill the layers. + :arg weights_type: The type on which the weights will be transformed in. - :ivar uff_required_version_major: :class:`int` Version Major of the UFF. - :ivar uff_required_version_minor: :class:`int` Version Minor of the UFF. - :ivar uff_required_version_patch: :class:`int` Version Patch of the UFF. - :ivar plugin_factory: :class:`IUffPluginFactory` used to create the user defined plugins. - :ivar plugin_factory_ext: :class:`IUffPluginFactoryExt` used to create the user defined pluginExts. - :ivar plugin_namespace: :class:`str` The namespace used to lookup and create plugins in the network. - )trtdoc"; + :returns: True if the UFF buffer is parsed without error. +)trtdoc"; +} // namespace UffParserDoc - constexpr const char* register_input = R"trtdoc( - Register an input name of a UFF network with the associated Dimensions. - - :arg name: Input name. - :arg shape: Input shape. - :arg order: Input order on which the framework input was originally. - - :returns: True if the name registers without error. - )trtdoc"; - - constexpr const char* register_output = R"trtdoc( - Register an output name of a UFF network. - - :arg output_name: Output name. - - :returns: True if the name registers without error. - )trtdoc"; - - constexpr const char* parse = R"trtdoc( - Parse a UFF file. - - :arg file: File name of the UFF file. - :arg network: Network in which the :class:`UffParser` will fill the layers. - :arg weights_type: The type on which the weights will be transformed in. - - :returns: True if the UFF file is parsed without error. - )trtdoc"; - - constexpr const char* parse_buffer = R"trtdoc( - Parse a UFF buffer - useful if the file is already live in memory. - - :arg buffer: The UFF buffer. - :arg network: Network in which the UFFParser will fill the layers. - :arg weights_type: The type on which the weights will be transformed in. - - :returns: True if the UFF buffer is parsed without error. - )trtdoc"; - } /* UffParserDoc */ - -} /* tensorrt */ +} // namespace tensorrt diff --git a/python/docstrings/pyTensorRTDoc.h b/python/docstrings/pyTensorRTDoc.h index f34411b7..49d5bb94 100644 --- a/python/docstrings/pyTensorRTDoc.h +++ b/python/docstrings/pyTensorRTDoc.h @@ -19,5 +19,5 @@ namespace tensorrt { - -} /* tensorrt */ + +} // namespace tensorrt diff --git a/python/include/ForwardDeclarations.h b/python/include/ForwardDeclarations.h index e58424d4..484e8fad 100644 --- a/python/include/ForwardDeclarations.h +++ b/python/include/ForwardDeclarations.h @@ -16,7 +16,12 @@ #pragma once #include + +#include "NvCaffeParser.h" #include "NvInfer.h" +#include "NvInferPlugin.h" +#include "NvUffParser.h" +#include "onnx/NvOnnxParser.h" // We need to avoid making copies of PluginField because it does not own any of it's members. // When there are multiple PluginFields pointing to the same data in Python, bad things happen. @@ -25,30 +30,43 @@ PYBIND11_MAKE_OPAQUE(std::vector); namespace tensorrt { - // Set some global namespace aliases. - namespace py = pybind11; - // This is for literal operators (like _a for default args) - using namespace pybind11::literals; - // Hack for situations where the C++ object does not own a member string/const char*. - // Cannot reference python strings, so we make a copy and keep it alive on the C++ side. - struct FallbackString { - FallbackString() = default; - FallbackString(std::string other) : mData{other} { } - FallbackString(py::str other) : mData{std::string(other)} { } - const char* c_str() const { return mData.c_str(); } - const char* c_str() { return mData.c_str(); } - std::string mData{}; - }; +// Set some global namespace aliases. +namespace py = pybind11; +// This is for literal operators (like _a for default args) +using namespace pybind11::literals; +// Hack for situations where the C++ object does not own a member string/const char*. +// Cannot reference python strings, so we make a copy and keep it alive on the C++ side. +struct FallbackString +{ + FallbackString() = default; + FallbackString(std::string other) + : mData{other} + { + } + FallbackString(py::str other) + : mData{std::string(other)} + { + } + const char* c_str() const + { + return mData.c_str(); + } + const char* c_str() + { + return mData.c_str(); + } + std::string mData{}; +}; - // Infer - void bindFoundationalTypes(py::module& m); - void bindPlugin(py::module& m); - void bindInt8(py::module& m); - void bindGraph(py::module& m); - void bindAlgorithm(py::module& m); - void bindCore(py::module& m); - // Parsers - void bindOnnx(py::module& m); - void bindUff(py::module& m); - void bindCaffe(py::module& m); -} /* tensorrt */ +// Infer +void bindFoundationalTypes(py::module& m); +void bindPlugin(py::module& m); +void bindInt8(py::module& m); +void bindGraph(py::module& m); +void bindAlgorithm(py::module& m); +void bindCore(py::module& m); +// Parsers +void bindOnnx(py::module& m); +void bindUff(py::module& m); +void bindCaffe(py::module& m); +} // namespace tensorrt diff --git a/python/include/utils.h b/python/include/utils.h index a79d9e24..fbf2771a 100644 --- a/python/include/utils.h +++ b/python/include/utils.h @@ -15,103 +15,163 @@ */ #pragma once -#include "NvInfer.h" -#include -// For array. #include +#include + +#include "NvInfer.h" +#include #include #include namespace tensorrt { - namespace utils +namespace utils +{ + +namespace py = pybind11; + +// Returns the size in bytes of the specified data type. +inline size_t size(nvinfer1::DataType type) +{ + switch (type) { + case nvinfer1::DataType::kFLOAT: return 4; + case nvinfer1::DataType::kHALF: return 2; + case nvinfer1::DataType::kINT8: return 1; + case nvinfer1::DataType::kINT32: return 4; + case nvinfer1::DataType::kBOOL: return 1; + } + return -1; +} - namespace py = pybind11; +// Converts a TRT datatype to its corresponding numpy dtype. +inline py::dtype nptype(nvinfer1::DataType type) +{ + switch (type) + { + case nvinfer1::DataType::kFLOAT: return py::dtype("f4"); + case nvinfer1::DataType::kHALF: return py::dtype("f2"); + case nvinfer1::DataType::kINT8: return py::dtype("i1"); + case nvinfer1::DataType::kINT32: return py::dtype("i4"); + case nvinfer1::DataType::kBOOL: return py::dtype("b1"); + } + return py::dtype("unknown"); +} - // Returns the size in bytes of the specified data type. - inline size_t size(nvinfer1::DataType type) { - switch (type) - { - case nvinfer1::DataType::kFLOAT: - return 4; - case nvinfer1::DataType::kHALF: - return 2; - case nvinfer1::DataType::kINT8: - return 1; - case nvinfer1::DataType::kINT32: - return 4; - case nvinfer1::DataType::kBOOL: - return 1; - } - return -1; - } +// Returns the TRT type corresponding to the specified numpy type. +inline nvinfer1::DataType type(const py::dtype& type) +{ + if (type.is(py::dtype("f4"))) + { + return nvinfer1::DataType::kFLOAT; + } + else if (type.is(py::dtype("f2"))) + { + return nvinfer1::DataType::kHALF; + } + else if (type.is(py::dtype("i4"))) + { + return nvinfer1::DataType::kINT32; + } + else if (type.is(py::dtype("i1"))) + { + return nvinfer1::DataType::kINT8; + } + else if (type.is(py::dtype("b1"))) + { + return nvinfer1::DataType::kBOOL; + } + std::cout << "[ERROR] Unsupported numpy data type: " << type.kind() << type.itemsize() * 8 + << ". Cannot implicitly convert to tensorrt.Weights." << std::endl; + throw std::invalid_argument{"Unsupported data type"}; +} - // Converts a TRT datatype to its corresponding numpy dtype. - inline py::dtype nptype(nvinfer1::DataType type) { - switch (type) { - case nvinfer1::DataType::kFLOAT: - return py::dtype("f4"); - case nvinfer1::DataType::kHALF: - return py::dtype("f2"); - case nvinfer1::DataType::kINT8: - return py::dtype("i1"); - case nvinfer1::DataType::kINT32: - return py::dtype("i4"); - case nvinfer1::DataType::kBOOL: - return py::dtype("b1"); - } - return py::dtype("unknown"); - } +// Return a numpy array (that doesn't own the data, but rather refers to it) +static const auto weights_to_numpy = [](const nvinfer1::Weights& self) { + // The py::cast(self) allows us to return the buffer by reference rather than by copy. + // See https://stackoverflow.com/questions/49181258/pybind11-create-numpy-view-of-data + return py::array{nptype(self.type), self.count, self.values, py::cast(self)}; +}; - // Returns the TRT type corresponding to the specified numpy type. - inline nvinfer1::DataType type(const py::dtype& type) { - if (type.is(py::dtype("f4"))) - { - return nvinfer1::DataType::kFLOAT; - } - else if (type.is(py::dtype("f2"))) - { - return nvinfer1::DataType::kHALF; - } - else if (type.is(py::dtype("i4"))) - { - return nvinfer1::DataType::kINT32; - } - else if (type.is(py::dtype("i1"))) - { - return nvinfer1::DataType::kINT8; - } - else if (type.is(py::dtype("b1"))) - { - return nvinfer1::DataType::kBOOL; - } - std::cout << "WARNING: Unsupported numpy data type. Cannot implicitly convert to tensorrt.Weights." << std::endl; - throw std::invalid_argument{"Unsupported data type"}; - } +inline size_t volume(const nvinfer1::Dims& dims) +{ + return std::accumulate(dims.d, dims.d + dims.nbDims, 1, std::multiplies()); +} - // Return a numpy array (that doesn't own the data, but rather refers to it) - static const auto weights_to_numpy = [] (const nvinfer1::Weights& self) { - // The py::cast(self) allows us to return the buffer by reference rather than by copy. - // See https://stackoverflow.com/questions/49181258/pybind11-create-numpy-view-of-data - return py::array{nptype(self.type), self.count, self.values, py::cast(self)}; - }; +// Method for calling the python function and returning the value (returned from python) used in cpp trampoline +// classes. Prints an error if no such method is overriden in python. +// T* must NOT be a trampoline class! +template +py::function getOverload(const T* self, const std::string& overloadName, bool showWarning = true) +{ + py::function overload = py::get_override(self, overloadName.c_str()); + if (!overload && showWarning) + { + std::cerr << "Method: " << overloadName + << " was not overriden. Please provide an implementation for this method."; + } + return overload; +} - inline size_t volume(const nvinfer1::Dims& dims) - { - return std::accumulate(dims.d, dims.d + dims.nbDims, 1, std::multiplies()); - } +// Deprecation helpers +void issueDeprecationWarning(const char* useInstead); - // Method for calling the python function and returning the value (returned from python) used in cpp trampoline classes. Throws an error if no such method is overriden in pyhton. - template - py::function getOverload(const T* self, const std::string& overloadName) - { - py::function overload = py::get_overload(self, overloadName.c_str()); - if (!overload) - { - throw std::runtime_error{"Method: " + overloadName + " was not overriden. Please provide an implementation for this method."}; - } - return overload; - } - } /* utils */ -} /* pynvinfer1 */ +// TODO: Figure out how to de-duplicate these two +template +struct DeprecatedFunc +{ + using Func = RetVal (*)(Args...); + + RetVal operator()(Args... args) const + { + issueDeprecationWarning(useInstead); + return (*func)(std::forward(args)...); + } + + const Func func; + const char* useInstead; +}; + +template +constexpr auto deprecate(RetVal (*func)(Args...), const char* useInstead) -> DeprecatedFunc +{ + return DeprecatedFunc{func, useInstead}; +} + +template +struct DeprecatedMemberFunc +{ + using Func = typename std::conditional::type; + + RetVal operator()(Cls& self, Args... args) const + { + issueDeprecationWarning(useInstead); + return (std::forward(self).*func)(std::forward(args)...); + } + + const Func func; + const char* useInstead; +}; + +template +constexpr auto deprecateMember(RetVal (Cls::*func)(Args...) const, const char* useInstead) + -> DeprecatedMemberFunc +{ + return DeprecatedMemberFunc{func, useInstead}; +} + +template +constexpr auto deprecateMember(RetVal (Cls::*func)(Args...), const char* useInstead) + -> DeprecatedMemberFunc +{ + return DeprecatedMemberFunc{func, useInstead}; +} + +template +void doNothingDel(const T& self) +{ + issueDeprecationWarning("del obj"); +} + +} // namespace utils +} // namespace tensorrt diff --git a/python/packaging/setup.py b/python/packaging/setup.py index c44c1b5a..f4144b73 100644 --- a/python/packaging/setup.py +++ b/python/packaging/setup.py @@ -30,17 +30,20 @@ def is_dla(): def get_requirements(): - def get_version_range(envvar): + def get_version_range(envvar, needs_exact_minor=False): vers = os.environ.get(envvar).replace("cuda-", "") major, minor = map(int, vers.split(".")) - return ">={major},<{major_next}".format(major=major, major_next=major + 1) + if needs_exact_minor: + return ">={major}.{minor},<{major}.{minor_next}".format(major=major, minor=minor, minor_next=minor + 1) + else: + return ">={major},<{major_next}".format(major=major, major_next=major + 1) if is_standalone(): return [ "nvidia-cuda-runtime" + get_version_range("CUDA"), "nvidia-cudnn" + get_version_range("CUDNN"), "nvidia-cublas" + get_version_range("CUDA"), - "nvidia-cuda-nvrtc" + get_version_range("CUDA"), + "nvidia-cuda-nvrtc" + get_version_range("CUDA", needs_exact_minor=True), ] return [] diff --git a/python/packaging/tensorrt/__init__.py b/python/packaging/tensorrt/__init__.py index 1ffa832e..8e939a04 100644 --- a/python/packaging/tensorrt/__init__.py +++ b/python/packaging/tensorrt/__init__.py @@ -17,6 +17,7 @@ import ctypes import glob import os +import warnings def try_load(library): @@ -33,22 +34,25 @@ for lib in glob.iglob(os.path.join(CURDIR, "*.so*")): from .tensorrt import * + __version__ = "##TENSORRT_VERSION##" -import sys -if sys.version_info.major == 2: - print("WARNING: TensorRT Python 2 support is deprecated, and will be dropped in a future version!") # Provides Python's `with` syntax def common_enter(this): + warnings.warn("Context managers for TensorRT types are deprecated. " + "Memory will be freed automatically when the reference count reaches 0.", + DeprecationWarning) return this + def common_exit(this, exc_type, exc_value, traceback): """ - Destroy this object, freeing all memory associated with it. This should be called to ensure that the object is cleaned up properly. - Equivalent to invoking :func:`__del__` + Context managers are deprecated and have no effect. Objects are automatically freed when + the reference count reaches 0. """ - this.__del__() + pass + # Logger does not have a destructor. ILogger.__enter__ = common_enter @@ -87,6 +91,7 @@ Refitter.__exit__ = common_exit IBuilderConfig.__enter__ = common_enter IBuilderConfig.__exit__ = common_exit + # Computes the volume of an iterable. def volume(iterable): """ @@ -101,6 +106,7 @@ def volume(iterable): vol *= elem return vol + # Converts a TensorRT datatype to the equivalent numpy type. def nptype(trt_type): ''' @@ -122,6 +128,7 @@ def nptype(trt_type): return mapping[trt_type] raise TypeError("Could not resolve TensorRT datatype to an equivalent numpy datatype.") + # Add a numpy-like itemsize property to the datatype. def _itemsize(trt_type): ''' diff --git a/python/src/infer/pyAlgorithmSelector.cpp b/python/src/infer/pyAlgorithmSelector.cpp index 93b4bd17..8223af11 100644 --- a/python/src/infer/pyAlgorithmSelector.cpp +++ b/python/src/infer/pyAlgorithmSelector.cpp @@ -15,112 +15,162 @@ */ // This contains the fundamental types, i.e. Dims, Weights, dtype -#include "NvInfer.h" -#include "utils.h" -#include "infer/pyAlgorithmSelectorDoc.h" #include "ForwardDeclarations.h" -#include +#include "utils.h" #include - +#include "infer/pyAlgorithmSelectorDoc.h" +#include +#include namespace tensorrt { - using namespace nvinfer1; +using namespace nvinfer1; - namespace lambda +namespace lambdas +{ +// For IAlgorithmContext +static const auto get_shape = [](IAlgorithmContext& self, int32_t index) -> std::vector { + std::vector shapes{}; + Dims minShape = self.getDimensions(index, OptProfileSelector::kMIN); + if (minShape.nbDims != -1) { - // For IAlgorithmContext - static const auto get_shape = [] (IAlgorithmContext& self, int32_t index) -> std::vector + shapes.emplace_back(minShape); + shapes.emplace_back(self.getDimensions(index, OptProfileSelector::kOPT)); + shapes.emplace_back(self.getDimensions(index, OptProfileSelector::kMAX)); + } + return shapes; +}; +} // namespace lambdas + +class IAlgorithmSelectorTrampoline : public IAlgorithmSelector +{ +public: + using IAlgorithmSelector::IAlgorithmSelector; + + virtual int32_t selectAlgorithms(const IAlgorithmContext& context, const IAlgorithm* const* choices, + int32_t nbChoices, int32_t* selection) noexcept override + { + py::gil_scoped_acquire gil{}; + + std::vector choicesVector; + std::copy(choices, choices + nbChoices, std::back_inserter(choicesVector)); + + py::function pySelectAlgorithms + = utils::getOverload(static_cast(this), "select_algorithms"); + if (!pySelectAlgorithms) { - std::vector shapes{}; - Dims minShape = self.getDimensions(index, OptProfileSelector::kMIN); - if (minShape.nbDims != -1) - { - shapes.emplace_back(minShape); - shapes.emplace_back(self.getDimensions(index, OptProfileSelector::kOPT)); - shapes.emplace_back(self.getDimensions(index, OptProfileSelector::kMAX)); - } - return shapes; - }; + return -1; + } - // For IAlgorithm - static const auto get_algorithm_io_info = [] (IAlgorithm& self, int32_t index) -> const IAlgorithmIOInfo& + py::object pyResult; + try { - return self.getAlgorithmIOInfo(index); - }; - } //lambda + pyResult = pySelectAlgorithms(&context, choicesVector); + } + catch (...) + { + std::cerr << "[ERROR] Exception caught in select_algorithms()" << std::endl; + return -1; + } - class IAlgorithmSelectorTrampoline : public IAlgorithmSelector + std::vector result; + try + { + result = pyResult.cast(); + } + catch (const py::cast_error& e) + { + std::cerr << "[ERROR] Return value of select_algorithms() could not be interpreted as a List[int]" + << std::endl; + return -1; + } + + std::copy(result.data(), result.data() + result.size(), selection); + return static_cast(result.size()); + } + + virtual void reportAlgorithms(const IAlgorithmContext* const* algoContexts, const IAlgorithm* const* algoChoices, + int32_t size) noexcept override { - public: - using IAlgorithmSelector::IAlgorithmSelector; + py::gil_scoped_acquire gil{}; - virtual int32_t selectAlgorithms(const IAlgorithmContext& context, const IAlgorithm* const* choices, int32_t nbChoices, int32_t* selection) override - { - py::gil_scoped_acquire gil{}; - py::function pySelectAlgorithms = utils::getOverload(this, "select_algorithms"); - std::vector choices_vector; - std::copy(choices, choices + nbChoices, std::back_inserter(choices_vector)); + std::vector contexts; + std::copy(algoContexts, algoContexts + size, std::back_inserter(contexts)); + std::vector choices; + std::copy(algoChoices, algoChoices + size, std::back_inserter(choices)); - py::object result_uncast = pySelectAlgorithms(&context, choices_vector); + py::function pyReportAlgorithms + = utils::getOverload(static_cast(this), "report_algorithms"); + if (!pyReportAlgorithms) + { + return; + } - std::pair> result = result_uncast.cast>>(); + try + { + pyReportAlgorithms(contexts, choices); + } + catch (...) + { + std::cerr << "[ERROR] Exception caught in report_algorithms()" << std::endl; + return; + } + } +}; // IAlgorithmSelectorTrampoline - int32_t ret_value = std::get<0>(result); - int32_t* selection_ptr = std::get<1>(result).data(); - std::copy(selection_ptr, selection_ptr + std::get<1>(result).size(), selection); - return ret_value; - } +// NOTE: Fake bindings are provided for some of the application-implemented functions here. +// These are solely for documentation purposes. The user is meant to override these functions +// in their own code, and the bindings here will never be called. - virtual void reportAlgorithms(const IAlgorithmContext* const* algoContexts, const IAlgorithm* const* algoChoices, int32_t size) override - { - py::gil_scoped_acquire gil{}; +std::vector select_algorithms( + IAlgorithmSelector&, const IAlgorithmContext&, const std::vector&) +{ + return {}; +} - std::vector contexts; - std::copy(algoContexts, algoContexts + size, std::back_inserter(contexts)); - std::vector choices; - std::copy(algoChoices, algoChoices + size, std::back_inserter(choices)); - py::function pyReportAlgorithms = utils::getOverload(this, "report_algorithms"); - pyReportAlgorithms(contexts, choices); +void report_algorithms( + IAlgorithmSelector&, const std::vector&, const std::vector&) +{ +} - } - }; // IAlgorithmSelectorTrampoline +void bindAlgorithm(py::module& m) +{ + // IAlgorithmIOInfo + py::class_>( + m, "IAlgorithmIOInfo", IAlgorithmIOInfoDOC::descr) + .def_property_readonly("tensor_format", &IAlgorithmIOInfo::getTensorFormat) + .def_property_readonly("dtype", &IAlgorithmIOInfo::getDataType) + .def_property_readonly("strides", &IAlgorithmIOInfo::getStrides); - void bindAlgorithm(py::module& m) - { - // IAlgorithmIOInfo - py::class_>(m, "IAlgorithmIOInfo", IAlgorithmIOInfoDOC::descr) - .def_property_readonly("tensor_format", &IAlgorithmIOInfo::getTensorFormat) - .def_property_readonly("dtype", &IAlgorithmIOInfo::getDataType) - .def_property_readonly("strides", &IAlgorithmIOInfo::getStrides) - ; + // IAlgorithmVariant + py::class_>( + m, "IAlgorithmVariant", IAlgorithmVariantDOC::descr) + .def_property_readonly("implementation", &IAlgorithmVariant::getImplementation) + .def_property_readonly("tactic", &IAlgorithmVariant::getTactic); - // IAlgorithmVariant - py::class_>(m, "IAlgorithmVariant", IAlgorithmVariantDOC::descr) - .def_property_readonly("implementation", &IAlgorithmVariant::getImplementation) - .def_property_readonly("tactic", &IAlgorithmVariant::getTactic) - ; + // IAlgorithmContext + py::class_>( + m, "IAlgorithmContext", IAlgorithmContextDoc::descr) + .def_property_readonly("name", &IAlgorithmContext::getName) + .def("get_shape", lambdas::get_shape, "index"_a, IAlgorithmContextDoc::get_shape) + .def_property_readonly("num_inputs", &IAlgorithmContext::getNbInputs) + .def_property_readonly("num_outputs", &IAlgorithmContext::getNbOutputs); - // IAlgorithmContext - py::class_>(m, "IAlgorithmContext", IAlgorithmContextDoc::descr) - .def_property_readonly("name", &IAlgorithmContext::getName) - .def("get_shape", lambda::get_shape, "index"_a, IAlgorithmContextDoc::get_shape) - .def_property_readonly("num_inputs", &IAlgorithmContext::getNbInputs) - .def_property_readonly("num_outputs", &IAlgorithmContext::getNbOutputs) - ; + // IAlgorithm + py::class_>(m, "IAlgorithm", IAlgorithmDoc::descr) + .def("get_algorithm_io_info", &IAlgorithm::getAlgorithmIOInfoByIndex, "index"_a, + IAlgorithmDoc::get_algorithm_io_info, py::return_value_policy::reference_internal) + .def_property_readonly("algorithm_variant", &IAlgorithm::getAlgorithmVariant) + .def_property_readonly("timing_msec", &IAlgorithm::getTimingMSec) + .def_property_readonly("workspace_size", &IAlgorithm::getWorkspaceSize); - // IAlgorithm - py::class_>(m, "IAlgorithm", IAlgorithmDoc::descr) - .def("get_algorithm_io_info", lambda::get_algorithm_io_info, "index"_a, IAlgorithmDoc::get_algorithm_io_info) - .def_property_readonly("algorithm_variant", &IAlgorithm::getAlgorithmVariant) - .def_property_readonly("timing_msec", &IAlgorithm::getTimingMSec) - .def_property_readonly("workspace_size", &IAlgorithm::getWorkspaceSize) - ; - - // IAlgorithmSelector - py::class_>(m, "IAlgorithmSelector", IAlgorithmSelectorDoc::descr) - .def(py::init_alias<>()) - ; - }// bindAlgorithm -} /* tensorrt */ + // IAlgorithmSelector + py::class_(m, "IAlgorithmSelector", IAlgorithmSelectorDoc::descr) + .def(py::init_alias<>()) // Always initialize trampoline class. + .def( + "select_algorithms", &select_algorithms, "context"_a, "choices"_a, IAlgorithmSelectorDoc::select_algorithms) + .def("report_algorithms", &report_algorithms, "contexts"_a, "choices"_a, + IAlgorithmSelectorDoc::report_algorithms); +} // bindAlgorithm +} // namespace tensorrt diff --git a/python/src/infer/pyCore.cpp b/python/src/infer/pyCore.cpp index 99b58db9..2ad14ae9 100644 --- a/python/src/infer/pyCore.cpp +++ b/python/src/infer/pyCore.cpp @@ -15,636 +15,802 @@ */ // This contains the core elements of the API, i.e. builder, logger, engine, runtime, context. -#include "NvInfer.h" -#include "utils.h" -#include "infer/pyCoreDoc.h" #include "ForwardDeclarations.h" -#include -// For vector support +#include "utils.h" #include +#include "infer/pyCoreDoc.h" +#include namespace tensorrt { - using namespace nvinfer1; - // Long lambda functions should go here rather than being inlined into the bindings (1 liners are OK). - namespace lambdas +using namespace nvinfer1; +// Long lambda functions should go here rather than being inlined into the bindings (1 liners are OK). +namespace lambdas +{ +// For IOptimizationProfile +static const auto opt_profile_set_shape + = [](IOptimizationProfile& self, const std::string& inputName, const Dims& min, const Dims& opt, const Dims& max) { + if (!self.setDimensions(inputName.c_str(), OptProfileSelector::kMIN, min)) + { + throw std::runtime_error{"Shape provided for min is inconsistent with other shapes."}; + } + if (!self.setDimensions(inputName.c_str(), OptProfileSelector::kOPT, opt)) + { + throw std::runtime_error{"Shape provided for opt is inconsistent with other shapes."}; + } + if (!self.setDimensions(inputName.c_str(), OptProfileSelector::kMAX, max)) + { + throw std::runtime_error{"Shape provided for max is inconsistent with other shapes."}; + } + }; + +static const auto opt_profile_get_shape + = [](IOptimizationProfile& self, const std::string& inputName) -> std::vector { + std::vector shapes{}; + Dims minShape = self.getDimensions(inputName.c_str(), OptProfileSelector::kMIN); + if (minShape.nbDims != -1) { - // For IOptimizationProfile - static const auto opt_profile_set_shape = [] (IOptimizationProfile& self, const std::string& inputName, const Dims& min, const Dims& opt, const Dims& max) - { - if (!self.setDimensions(inputName.c_str(), OptProfileSelector::kMIN, min)) - { - throw std::runtime_error{"Shape provided for min is inconsistent with other shapes."}; - } - if (!self.setDimensions(inputName.c_str(), OptProfileSelector::kOPT, opt)) - { - throw std::runtime_error{"Shape provided for opt is inconsistent with other shapes."}; - } - if (!self.setDimensions(inputName.c_str(), OptProfileSelector::kMAX, max)) - { - throw std::runtime_error{"Shape provided for max is inconsistent with other shapes."}; - } - }; + shapes.emplace_back(minShape); + shapes.emplace_back(self.getDimensions(inputName.c_str(), OptProfileSelector::kOPT)); + shapes.emplace_back(self.getDimensions(inputName.c_str(), OptProfileSelector::kMAX)); + } + return shapes; +}; - static const auto opt_profile_get_shape = [] (IOptimizationProfile& self, const std::string& inputName) -> std::vector - { - std::vector shapes{}; - Dims minShape = self.getDimensions(inputName.c_str(), OptProfileSelector::kMIN); - if (minShape.nbDims != -1) - { - shapes.emplace_back(minShape); - shapes.emplace_back(self.getDimensions(inputName.c_str(), OptProfileSelector::kOPT)); - shapes.emplace_back(self.getDimensions(inputName.c_str(), OptProfileSelector::kMAX)); - } - return shapes; - }; +static const auto opt_profile_set_shape_input + = [](IOptimizationProfile& self, const std::string& inputName, const std::vector& min, + const std::vector& opt, const std::vector& max) { + if (!self.setShapeValues(inputName.c_str(), OptProfileSelector::kMIN, min.data(), min.size())) + { + throw std::runtime_error{"min input provided for shape tensor is inconsistent with other inputs."}; + } + if (!self.setShapeValues(inputName.c_str(), OptProfileSelector::kOPT, opt.data(), opt.size())) + { + throw std::runtime_error{"opt input provided for shape tensor is inconsistent with other inputs."}; + } + if (!self.setShapeValues(inputName.c_str(), OptProfileSelector::kMAX, max.data(), max.size())) + { + throw std::runtime_error{"max input provided for shape tensor is inconsistent with other inputs."}; + } + }; - static const auto opt_profile_set_shape_input = [] (IOptimizationProfile& self, const std::string& inputName, const std::vector& min, const std::vector& opt, const std::vector& max) - { - if (!self.setShapeValues(inputName.c_str(), OptProfileSelector::kMIN, min.data(), min.size())) - { - throw std::runtime_error{"min input provided for shape tensor is inconsistent with other inputs."}; - } - if (!self.setShapeValues(inputName.c_str(), OptProfileSelector::kOPT, opt.data(), opt.size())) - { - throw std::runtime_error{"opt input provided for shape tensor is inconsistent with other inputs."}; - } - if (!self.setShapeValues(inputName.c_str(), OptProfileSelector::kMAX, max.data(), max.size())) - { - throw std::runtime_error{"max input provided for shape tensor is inconsistent with other inputs."}; - } - }; - - static const auto opt_profile_get_shape_input = [] (IOptimizationProfile& self, const std::string& inputName) -> std::vector> - { - std::vector> shapes{}; - int shapeSize = self.getNbShapeValues(inputName.c_str()); - const int32_t* shapePtr = self.getShapeValues(inputName.c_str(), OptProfileSelector::kMIN); - // In the Python bindings, it is impossible to set only one shape in an optimization profile. - if (shapePtr && shapeSize >= 0) - { - shapes.emplace_back(shapePtr, shapePtr + shapeSize); - if (!(shapePtr = self.getShapeValues(inputName.c_str(), OptProfileSelector::kOPT))) - { - throw std::runtime_error{"Invalid shape for OPT."}; - } - shapes.emplace_back(shapePtr, shapePtr + shapeSize); - if (!(shapePtr = self.getShapeValues(inputName.c_str(), OptProfileSelector::kMAX))) - { - throw std::runtime_error{"Invalid shape for MAX."}; - } - shapes.emplace_back(shapePtr, shapePtr + shapeSize); - } - return shapes; - }; - - // For IExecutionContext - static const auto execute = [](IExecutionContext& self, int batchSize, std::vector& bindings) - { - return self.execute(batchSize, reinterpret_cast(bindings.data())); - }; - - static const auto execute_async = [](IExecutionContext& self, int batchSize, std::vector& bindings, size_t streamHandle, void* inputConsumed) - { - return self.enqueue(batchSize, reinterpret_cast(bindings.data()), - reinterpret_cast(streamHandle), reinterpret_cast(inputConsumed)); - }; - - static const auto execute_v2 = [](IExecutionContext& self, std::vector& bindings) - { - return self.executeV2(reinterpret_cast(bindings.data())); - }; - - static const auto execute_async_v2 = [](IExecutionContext& self, std::vector& bindings, size_t streamHandle, void* inputConsumed) - { - return self.enqueueV2(reinterpret_cast(bindings.data()), - reinterpret_cast(streamHandle), reinterpret_cast(inputConsumed)); - }; - - static const auto context_set_optimization_profile = [](IExecutionContext& self, int profileIndex) { - if (!self.setOptimizationProfile(profileIndex)) - { - throw std::runtime_error{"Error in set optimization profile."}; - } - }; - - static const auto context_set_shape_input = [] (IExecutionContext& self, int binding, const std::vector& shape) - { - return self.setInputShapeBinding(binding, shape.data()); - }; - - static const auto context_get_shape = [] (IExecutionContext& self, int binding) - { - Dims shapeOfShape = self.getBindingDimensions(binding); - int numVals = std::accumulate(shapeOfShape.d, shapeOfShape.d + shapeOfShape.nbDims, 1, std::multiplies{}); - std::vector shape(numVals); - if (!self.getShapeBinding(binding, shape.data())) - { - throw std::runtime_error{"Error in get shape bindings."}; - } - return shape; - }; - - // For IRuntime - static const auto runtime_deserialize_cuda_engine = [] (IRuntime& self, py::buffer& serializedEngine, IPluginFactory* pluginFactory = nullptr) - { - py::buffer_info info = serializedEngine.request(); - return self.deserializeCudaEngine(info.ptr, info.size * info.itemsize, pluginFactory); - }; - - // For ICudaEngine - static const auto engine_binding_is_input = [] (ICudaEngine& self, const std::string& name) - { - return self.bindingIsInput(self.getBindingIndex(name.c_str())); - }; - - static const auto engine_get_binding_shape = [] (ICudaEngine& self, const std::string& name) - { - return self.getBindingDimensions(self.getBindingIndex(name.c_str())); - }; - - static const auto engine_get_binding_dtype = [] (ICudaEngine& self, const std::string& name) - { - return self.getBindingDataType(self.getBindingIndex(name.c_str())); - }; - - static const auto engine_get_location = [] (ICudaEngine& self, const std::string& name) - { - return self.getLocation(self.getBindingIndex(name.c_str())); - }; - - static const auto engine_getitem = [] (ICudaEngine& self, int pyIndex) - { - // Support python's negative indexing - size_t index = (pyIndex < 0) ? static_cast(self.getNbBindings()) + pyIndex : pyIndex; - if (index >= self.getNbBindings()) throw py::index_error(); - return self.getBindingName(index); - }; - - static const auto engine_get_profile_shape = [] (ICudaEngine& self, int profileIndex, int bindingIndex) -> std::vector - { - std::vector shapes{}; - shapes.emplace_back(self.getProfileDimensions(bindingIndex, profileIndex, OptProfileSelector::kMIN)); - shapes.emplace_back(self.getProfileDimensions(bindingIndex, profileIndex, OptProfileSelector::kOPT)); - shapes.emplace_back(self.getProfileDimensions(bindingIndex, profileIndex, OptProfileSelector::kMAX)); - return shapes; - }; - // Overload to allow using binding names instead of indices. - static const auto engine_get_profile_shape_str = [] (ICudaEngine& self, int profileIndex, const std::string& bindingName) -> std::vector - { - return engine_get_profile_shape(self, profileIndex, self.getBindingIndex(bindingName.c_str())); - }; - - static const auto engine_get_profile_shape_input = [] (ICudaEngine& self, int profileIndex, int bindingIndex) -> std::vector> - { - if (!self.isShapeBinding(bindingIndex) || !self.bindingIsInput(bindingIndex)) - { - throw std::runtime_error{"Binding index " + std::to_string(bindingIndex) + " does not correspond to an input shape tensor."}; - } - std::vector> shapes{}; - int shapeSize = self.getBindingDimensions(bindingIndex).nbDims; - // In the Python bindings, it is impossible to set only one shape in an optimization profile. - const int32_t* shapePtr = self.getProfileShapeValues(bindingIndex, profileIndex, OptProfileSelector::kMIN); - if (shapePtr) - { - shapes.emplace_back(shapePtr, shapePtr + shapeSize); - shapePtr = self.getProfileShapeValues(bindingIndex, profileIndex, OptProfileSelector::kOPT); - shapes.emplace_back(shapePtr, shapePtr + shapeSize); - shapePtr = self.getProfileShapeValues(bindingIndex, profileIndex, OptProfileSelector::kMAX); - shapes.emplace_back(shapePtr, shapePtr + shapeSize); - } - return shapes; - }; - - // Overload to allow using binding names instead of indices. - static const auto engine_get_profile_shape_input_str = []( - ICudaEngine& self, int profileIndex, const std::string& bindingName) -> std::vector> { - return engine_get_profile_shape_input(self, profileIndex, self.getBindingIndex(bindingName.c_str())); - }; - - // For IBuilderConfig - static const auto netconfig_get_profile_stream - = [](IBuilderConfig& self) -> size_t { return reinterpret_cast(self.getProfileStream()); }; - - static const auto netconfig_set_profile_stream = [](IBuilderConfig& self, size_t streamHandle) { - self.setProfileStream(reinterpret_cast(streamHandle)); - }; - - // For IRefitter - static const auto refitter_get_missing = [] (IRefitter& self) - { - // First get the number of missing weights. - int size = self.getMissing(0, nullptr, nullptr); - // Now that we know how many weights are missing, we can create the buffers appropriately. - std::vector layerNames(size); - std::vector roles(size); - self.getMissing(size, layerNames.data(), roles.data()); - return std::pair, std::vector>{layerNames, roles}; - }; - - static const auto refitter_get_all = [] (IRefitter& self) - { - int size = self.getAll(0, nullptr, nullptr); - std::vector layerNames(size); - std::vector roles(size); - self.getAll(size, layerNames.data(), roles.data()); - return std::pair, std::vector>{layerNames, roles}; - }; - - static const auto refitter_get_dynamic_range = [] (IRefitter& self, const std::string& tensorName) - { - return py::make_tuple(self.getDynamicRangeMin(tensorName.c_str()), self.getDynamicRangeMax(tensorName.c_str())); - }; - - static const auto refitter_set_dynamic_range = [] (IRefitter& self, const std::string& tensorName, const std::vector& range) -> bool - { - if (range.size() == 2) - { - return self.setDynamicRange(tensorName.c_str(), range[0], range[1]); - } - else - { - throw py::value_error{"Dynamic range must contain exactly 2 elements"}; - } - }; - - static const auto refitter_get_tensors_with_dynamic_range = [] (IRefitter& self) - { - int size = self.getTensorsWithDynamicRange(0, nullptr); - std::vector tensorNames(size); - self.getTensorsWithDynamicRange(size, tensorNames.data()); - return tensorNames; - }; - - static const auto context_set_optimization_profile_async = [](IExecutionContext& self, int profileIndex, size_t streamHandle) { - if (!self.setOptimizationProfileAsync(profileIndex, reinterpret_cast(streamHandle))) - { - throw std::runtime_error{"Error in set optimization profile async."}; - }; - return true; - }; - - } /* lambdas */ - - void bindCore(py::module& m) +static const auto opt_profile_get_shape_input + = [](IOptimizationProfile& self, const std::string& inputName) -> std::vector> { + std::vector> shapes{}; + int shapeSize = self.getNbShapeValues(inputName.c_str()); + const int32_t* shapePtr = self.getShapeValues(inputName.c_str(), OptProfileSelector::kMIN); + // In the Python bindings, it is impossible to set only one shape in an optimization profile. + if (shapePtr && shapeSize >= 0) { - // Provide a base implementation of a logger. - class PyLogger : public ILogger + shapes.emplace_back(shapePtr, shapePtr + shapeSize); + if (!(shapePtr = self.getShapeValues(inputName.c_str(), OptProfileSelector::kOPT))) { - public: - PyLogger(Severity minSeverity = Severity::kWARNING) : mMinSeverity(minSeverity) { } - - virtual void log(Severity severity, const char* msg) override - { - // INFO is the largest value, so this comparison is inverted. - if (severity > mMinSeverity) return; - - std::string loggingPrefix = "[TensorRT] "; - if (severity == Severity::kINTERNAL_ERROR) - loggingPrefix += "INTERNAL ERROR: "; - else if (severity == Severity::kERROR) - loggingPrefix += "ERROR: "; - else if (severity == Severity::kWARNING) - loggingPrefix += "WARNING: "; - else if (severity == Severity::kINFO) - loggingPrefix += "INFO: "; - else if (severity == Severity::kVERBOSE) - loggingPrefix += "VERBOSE: "; - std::cerr << loggingPrefix << msg << std::endl; - } - - Severity mMinSeverity; - }; - - // Expose the base class to pybind11. - py::class_>(m, "ILogger"); - // Provide a base logger class that will log to stderr. - // Need to instantiate so we can put the Severity enum under PyLogger. - py::class_> loggerBinding(m, "Logger", LoggerDoc::descr); - - // py::arithmetic() allows us to compare severities with < and > - py::enum_(loggerBinding, "Severity", py::arithmetic()) - .value("INTERNAL_ERROR", ILogger::Severity::kINTERNAL_ERROR, SeverityDoc::internal_error) - .value("ERROR", ILogger::Severity::kERROR, SeverityDoc::error) - .value("WARNING", ILogger::Severity::kWARNING, SeverityDoc::warning) - .value("INFO", ILogger::Severity::kINFO, SeverityDoc::info) - .value("VERBOSE", ILogger::Severity::kVERBOSE, SeverityDoc::verbose) - // We export into the parent class, so we can access with trt.ILogger.X. - // Importantly, we can STILL access values with trt.ILogger.Severity.X. - .export_values() - ; - - // Need to do this after, so that the severity enum is available. - loggerBinding - .def(py::init(), "min_severity"_a = ILogger::Severity::kWARNING) - .def_readwrite("min_severity", &PyLogger::mMinSeverity) - .def("log", &PyLogger::log, "severity"_a, "msg"_a, LoggerDoc::log) - ; - - // Provide a base implementation of a profiler. - class PyProfiler : public IProfiler + throw std::runtime_error{"Invalid shape for OPT."}; + } + shapes.emplace_back(shapePtr, shapePtr + shapeSize); + if (!(shapePtr = self.getShapeValues(inputName.c_str(), OptProfileSelector::kMAX))) { - public: - void reportLayerTime(const char* layerName, float ms) override - { - std::cout << layerName << ": " << ms << "ms" << std::endl; - } - }; + throw std::runtime_error{"Invalid shape for MAX."}; + } + shapes.emplace_back(shapePtr, shapePtr + shapeSize); + } + return shapes; +}; - // Expose the base class to pybind11. - py::class_>(m, "IProfiler"); - // Provide a base profiler class that will write to stdout. - py::class_>(m, "Profiler", ProfilerDoc::descr) - .def(py::init<>()) - .def("report_layer_time", &PyProfiler::reportLayerTime, "layer_name"_a, "ms"_a, ProfilerDoc::report_layer_time) - ; +// For IExecutionContext +static const auto execute = [](IExecutionContext& self, int batchSize, std::vector& bindings) { + return self.execute(batchSize, reinterpret_cast(bindings.data())); +}; - py::class_ >(m, "IOptimizationProfile", IOptimizationProfileDoc::descr) - .def("set_shape", lambdas::opt_profile_set_shape, "input"_a, "min"_a, "opt"_a, "max"_a, IOptimizationProfileDoc::set_shape) - .def("get_shape", lambdas::opt_profile_get_shape, "input"_a, IOptimizationProfileDoc::get_shape) - .def("set_shape_input", lambdas::opt_profile_set_shape_input, "input"_a, "min"_a, "opt"_a, "max"_a, IOptimizationProfileDoc::set_shape_input) - .def("get_shape_input", lambdas::opt_profile_get_shape_input, "input"_a, IOptimizationProfileDoc::get_shape_input) - .def_property("extra_memory_target", &IOptimizationProfile::getExtraMemoryTarget, &IOptimizationProfile::setExtraMemoryTarget) - .def("__nonzero__", &IOptimizationProfile::isValid) - .def("__bool__", &IOptimizationProfile::isValid) - ; +static const auto execute_async = [](IExecutionContext& self, int batchSize, std::vector& bindings, + size_t streamHandle, void* inputConsumed) { + return self.enqueue(batchSize, reinterpret_cast(bindings.data()), + reinterpret_cast(streamHandle), reinterpret_cast(inputConsumed)); +}; - py::enum_(m, "ErrorCodeTRT", py::arithmetic{}, ErrorCodeDoc::descr) - .value("SUCCESS", ErrorCode::kSUCCESS, ErrorCodeDoc::SUCCESS) - .value("UNSPECIFIED_ERROR", ErrorCode::kUNSPECIFIED_ERROR, ErrorCodeDoc::UNSPECIFIED_ERROR) - .value("INTERNAL_ERROR", ErrorCode::kINTERNAL_ERROR, ErrorCodeDoc::INTERNAL_ERROR) - .value("INVALID_ARGUMENT", ErrorCode::kINVALID_ARGUMENT, ErrorCodeDoc::INVALID_ARGUMENT) - .value("INVALID_CONFIG", ErrorCode::kINVALID_CONFIG, ErrorCodeDoc::INVALID_CONFIG) - .value("FAILED_ALLOCATION", ErrorCode::kFAILED_ALLOCATION, ErrorCodeDoc::FAILED_ALLOCATION) - .value("FAILED_INITIALIZATION", ErrorCode::kFAILED_INITIALIZATION, ErrorCodeDoc::FAILED_INITIALIZATION) - .value("FAILED_EXECUTION", ErrorCode::kFAILED_EXECUTION, ErrorCodeDoc::FAILED_EXECUTION) - .value("FAILED_COMPUTATION", ErrorCode::kFAILED_COMPUTATION, ErrorCodeDoc::FAILED_COMPUTATION) - .value("INVALID_STATE", ErrorCode::kINVALID_STATE, ErrorCodeDoc::INVALID_STATE) - .value("UNSUPPORTED_STATE", ErrorCode::kUNSUPPORTED_STATE, ErrorCodeDoc::UNSUPPORTED_STATE) - ; +static const auto execute_v2 = [](IExecutionContext& self, std::vector& bindings) { + return self.executeV2(reinterpret_cast(bindings.data())); +}; - // Provide a base implementation of Error recorder. - // Trampoline class is required as this class needs to be implemented by user. - class PyErrorRecorder : public IErrorRecorder +static const auto execute_async_v2 + = [](IExecutionContext& self, std::vector& bindings, size_t streamHandle, void* inputConsumed) { + return self.enqueueV2(reinterpret_cast(bindings.data()), reinterpret_cast(streamHandle), + reinterpret_cast(inputConsumed)); + }; + +void context_set_optimization_profile(IExecutionContext& self, int32_t profileIndex) +{ + if (!self.setOptimizationProfile(profileIndex)) + { + throw std::runtime_error{"Error in set optimization profile."}; + } +}; + +static const auto context_set_shape_input + = [](IExecutionContext& self, int binding, const std::vector& shape) { + return self.setInputShapeBinding(binding, shape.data()); + }; + +static const auto context_get_shape = [](IExecutionContext& self, int binding) { + Dims shapeOfShape = self.getBindingDimensions(binding); + int numVals = std::accumulate(shapeOfShape.d, shapeOfShape.d + shapeOfShape.nbDims, 1, std::multiplies{}); + std::vector shape(numVals); + if (!self.getShapeBinding(binding, shape.data())) + { + throw std::runtime_error{"Error in get shape bindings."}; + } + return shape; +}; + +// For IRuntime +static const auto runtime_deserialize_cuda_engine = [](IRuntime& self, py::buffer& serializedEngine) { + py::buffer_info info = serializedEngine.request(); + return self.deserializeCudaEngine(info.ptr, info.size * info.itemsize); +}; + +// For ICudaEngine +static const auto engine_binding_is_input = [](ICudaEngine& self, const std::string& name) { + return self.bindingIsInput(self.getBindingIndex(name.c_str())); +}; + +static const auto engine_get_binding_shape = [](ICudaEngine& self, const std::string& name) { + return self.getBindingDimensions(self.getBindingIndex(name.c_str())); +}; + +static const auto engine_get_binding_dtype = [](ICudaEngine& self, const std::string& name) { + return self.getBindingDataType(self.getBindingIndex(name.c_str())); +}; + +static const auto engine_get_location + = [](ICudaEngine& self, const std::string& name) { return self.getLocation(self.getBindingIndex(name.c_str())); }; + +// TODO: Add slicing support? +static const auto engine_getitem = [](ICudaEngine& self, int pyIndex) { + // Support python's negative indexing + size_t index = (pyIndex < 0) ? static_cast(self.getNbBindings()) + pyIndex : pyIndex; + if (index >= self.getNbBindings()) + throw py::index_error(); + return self.getBindingName(index); +}; + +static const auto engine_get_profile_shape + = [](ICudaEngine& self, int profileIndex, int bindingIndex) -> std::vector { + std::vector shapes{}; + shapes.emplace_back(self.getProfileDimensions(bindingIndex, profileIndex, OptProfileSelector::kMIN)); + shapes.emplace_back(self.getProfileDimensions(bindingIndex, profileIndex, OptProfileSelector::kOPT)); + shapes.emplace_back(self.getProfileDimensions(bindingIndex, profileIndex, OptProfileSelector::kMAX)); + return shapes; +}; +// Overload to allow using binding names instead of indices. +static const auto engine_get_profile_shape_str + = [](ICudaEngine& self, int profileIndex, const std::string& bindingName) -> std::vector { + return engine_get_profile_shape(self, profileIndex, self.getBindingIndex(bindingName.c_str())); +}; + +static const auto engine_get_profile_shape_input + = [](ICudaEngine& self, int profileIndex, int bindingIndex) -> std::vector> { + if (!self.isShapeBinding(bindingIndex) || !self.bindingIsInput(bindingIndex)) + { + throw std::runtime_error{ + "Binding index " + std::to_string(bindingIndex) + " does not correspond to an input shape tensor."}; + } + std::vector> shapes{}; + int shapeSize = self.getBindingDimensions(bindingIndex).nbDims; + // In the Python bindings, it is impossible to set only one shape in an optimization profile. + const int32_t* shapePtr = self.getProfileShapeValues(bindingIndex, profileIndex, OptProfileSelector::kMIN); + if (shapePtr) + { + shapes.emplace_back(shapePtr, shapePtr + shapeSize); + shapePtr = self.getProfileShapeValues(bindingIndex, profileIndex, OptProfileSelector::kOPT); + shapes.emplace_back(shapePtr, shapePtr + shapeSize); + shapePtr = self.getProfileShapeValues(bindingIndex, profileIndex, OptProfileSelector::kMAX); + shapes.emplace_back(shapePtr, shapePtr + shapeSize); + } + return shapes; +}; + +// Overload to allow using binding names instead of indices. +static const auto engine_get_profile_shape_input_str + = [](ICudaEngine& self, int profileIndex, const std::string& bindingName) -> std::vector> { + return engine_get_profile_shape_input(self, profileIndex, self.getBindingIndex(bindingName.c_str())); +}; + +// For IBuilderConfig +static const auto netconfig_get_profile_stream + = [](IBuilderConfig& self) -> size_t { return reinterpret_cast(self.getProfileStream()); }; + +static const auto netconfig_set_profile_stream = [](IBuilderConfig& self, size_t streamHandle) { + self.setProfileStream(reinterpret_cast(streamHandle)); +}; + +static const auto netconfig_create_timing_cache = [](IBuilderConfig& self, py::buffer& serializedTimingCache) { + py::buffer_info info = serializedTimingCache.request(); + return self.createTimingCache(info.ptr, info.size * info.itemsize); +}; + +// For IRefitter +static const auto refitter_get_missing = [](IRefitter& self) { + // First get the number of missing weights. + int size = self.getMissing(0, nullptr, nullptr); + // Now that we know how many weights are missing, we can create the buffers appropriately. + std::vector layerNames(size); + std::vector roles(size); + self.getMissing(size, layerNames.data(), roles.data()); + return std::pair, std::vector>{layerNames, roles}; +}; + +static const auto refitter_get_missing_weights = [](IRefitter& self) { + // First get the number of missing weights. + int size = self.getMissingWeights(0, nullptr); + // Now that we know how many weights are missing, we can create the buffers appropriately. + std::vector names(size); + self.getMissingWeights(size, names.data()); + return names; +}; + +static const auto refitter_get_all = [](IRefitter& self) { + int size = self.getAll(0, nullptr, nullptr); + std::vector layerNames(size); + std::vector roles(size); + self.getAll(size, layerNames.data(), roles.data()); + return std::pair, std::vector>{layerNames, roles}; +}; + +static const auto refitter_get_all_weights = [](IRefitter& self) { + int size = self.getAllWeights(0, nullptr); + std::vector names(size); + self.getAllWeights(size, names.data()); + return names; +}; + +static const auto refitter_get_dynamic_range = [](IRefitter& self, const std::string& tensorName) { + return py::make_tuple(self.getDynamicRangeMin(tensorName.c_str()), self.getDynamicRangeMax(tensorName.c_str())); +}; + +static const auto refitter_set_dynamic_range + = [](IRefitter& self, const std::string& tensorName, const std::vector& range) -> bool { + if (range.size() == 2) + { + return self.setDynamicRange(tensorName.c_str(), range[0], range[1]); + } + else + { + throw py::value_error{"Dynamic range must contain exactly 2 elements"}; + } +}; + +static const auto refitter_get_tensors_with_dynamic_range = [](IRefitter& self) { + int size = self.getTensorsWithDynamicRange(0, nullptr); + std::vector tensorNames(size); + self.getTensorsWithDynamicRange(size, tensorNames.data()); + return tensorNames; +}; + +static const auto context_set_optimization_profile_async + = [](IExecutionContext& self, int profileIndex, size_t streamHandle) { + if (!self.setOptimizationProfileAsync(profileIndex, reinterpret_cast(streamHandle))) + { + throw std::runtime_error{"Error in set optimization profile async."}; + }; + return true; + }; + +} // namespace lambdas + +class PyGpuAllocator : public IGpuAllocator +{ +public: + using IGpuAllocator::IGpuAllocator; + + template + void* allocHelper(const char* pyFuncName, bool showWarning, Args&&... args) noexcept + { + py::gil_scoped_acquire gil{}; + py::function pyAllocFunc = utils::getOverload(static_cast(this), pyFuncName, showWarning); + + if (!pyAllocFunc) { - public: - virtual ErrorCode getErrorCode(int32_t errorIdx) const noexcept override - { - PYBIND11_OVERLOAD_PURE_NAME(ErrorCode, IErrorRecorder, "get_error_code", getErrorCode, errorIdx); - } + return nullptr; + } - virtual ErrorDesc getErrorDesc(int32_t errorIdx) const noexcept override - { - PYBIND11_OVERLOAD_PURE_NAME(ErrorDesc, IErrorRecorder, "get_error_desc", getErrorDesc, errorIdx); - } + py::object ptr{}; + try + { + ptr = pyAllocFunc(std::forward(args)...); + } + catch (...) + { + std::cerr << "[ERROR] Exception caught in allocate()" << std::endl; + return nullptr; + } - virtual void clear() noexcept override - { - PYBIND11_OVERLOAD_PURE_NAME(void, IErrorRecorder, "clear", clear); - } + try + { + return reinterpret_cast(ptr.cast()); + } + catch (const py::cast_error& e) + { + std::cerr << "[ERROR] Return value of allocate() could not be interpreted as an int" << std::endl; + } - virtual bool reportError(ErrorCode val, ErrorDesc desc) noexcept override - { - PYBIND11_OVERLOAD_PURE_NAME(bool, IErrorRecorder, "report_error", reportError, val, desc); - } - - virtual int32_t getNbErrors() const noexcept override - { - PYBIND11_OVERLOAD_PURE_NAME(int32_t, IErrorRecorder, "get_num_errors", getNbErrors); - } - - virtual bool hasOverflowed() const noexcept override - { - PYBIND11_OVERLOAD_PURE_NAME(bool, IErrorRecorder, "has_overflowed", hasOverflowed); - } - - virtual RefCount incRefCount() noexcept override {} - virtual RefCount decRefCount() noexcept override {} - }; - - py::class_(m, "IErrorRecorder", IErrorRecorderDoc::descr) - .def(py::init<>()) - .def("num_errors", &IErrorRecorder::getNbErrors, IErrorRecorderDoc::get_num_errors) - .def("get_error_code", &IErrorRecorder::getErrorCode, IErrorRecorderDoc::get_error_code) - .def("get_error_desc", &IErrorRecorder::getErrorDesc, IErrorRecorderDoc::get_error_desc) - .def("has_overflowed", &IErrorRecorder::hasOverflowed, IErrorRecorderDoc::has_overflowed) - .def("clear", &IErrorRecorder::clear, IErrorRecorderDoc::clear) - .def("report_error", &IErrorRecorder::reportError, IErrorRecorderDoc::report_error) - ; - - py::class_ >(m, "IExecutionContext", IExecutionContextDoc::descr) - .def("execute", lambdas::execute, "batch_size"_a=1, "bindings"_a, IExecutionContextDoc::execute) - .def("execute_async", lambdas::execute_async, "batch_size"_a=1, "bindings"_a, "stream_handle"_a, "input_consumed"_a = nullptr, IExecutionContextDoc::execute_async) - .def("execute_v2", lambdas::execute_v2, "bindings"_a, IExecutionContextDoc::execute_v2) - .def("execute_async_v2", lambdas::execute_async_v2, "bindings"_a, "stream_handle"_a, "input_consumed"_a = nullptr, IExecutionContextDoc::execute_async_v2) - .def_property("debug_sync", &IExecutionContext::getDebugSync, &IExecutionContext::setDebugSync) - .def_property("profiler", &IExecutionContext::getProfiler, py::cpp_function(&IExecutionContext::setProfiler, py::keep_alive<1, 2>{})) - .def_property_readonly("engine", &IExecutionContext::getEngine) - .def_property("name", &IExecutionContext::getName, py::cpp_function(&IExecutionContext::setName, py::keep_alive<1, 2>{})) - // For writeonly properties, we use a nullptr getter. - .def_property("device_memory", nullptr, &IExecutionContext::setDeviceMemory) - .def_property("active_optimization_profile", &IExecutionContext::getOptimizationProfile, lambdas::context_set_optimization_profile) - .def("get_strides", &IExecutionContext::getStrides, "binding"_a, IExecutionContextDoc::get_strides) - .def("set_binding_shape", &IExecutionContext::setBindingDimensions, "binding"_a, "shape"_a, IExecutionContextDoc::set_binding_shape) - .def("get_binding_shape", &IExecutionContext::getBindingDimensions, "binding"_a, IExecutionContextDoc::get_binding_shape) - .def("set_shape_input", lambdas::context_set_shape_input, "binding"_a, "shape"_a, IExecutionContextDoc::set_shape_input) - .def("get_shape", lambdas::context_get_shape, "binding"_a, IExecutionContextDoc::get_shape) - .def_property_readonly("all_binding_shapes_specified", &IExecutionContext::allInputDimensionsSpecified) - .def_property_readonly("all_shape_inputs_specified", &IExecutionContext::allInputShapesSpecified) - .def("set_optimization_profile_async", lambdas::context_set_optimization_profile_async, "profile_index"_a, "stream_handle"_a, - IExecutionContextDoc::set_optimization_profile_async) - .def("__del__", &IExecutionContext::destroy) - ; - - py::class_ >(m, "ICudaEngine", ICudaEngineDoc::descr) - .def_property_readonly("num_bindings", &ICudaEngine::getNbBindings) - .def("__len__", &ICudaEngine::getNbBindings) - .def("__getitem__", [] (ICudaEngine& self, const std::string& name) { return self.getBindingIndex(name.c_str());}) - .def("__getitem__", lambdas::engine_getitem) - .def("get_binding_name", &ICudaEngine::getBindingName, "index"_a, ICudaEngineDoc::get_binding_name) - .def("get_binding_index", &ICudaEngine::getBindingIndex, "name"_a, ICudaEngineDoc::get_binding_index) - .def("binding_is_input", &ICudaEngine::bindingIsInput, "index"_a, ICudaEngineDoc::binding_is_input) - .def("binding_is_input", lambdas::engine_binding_is_input, "name"_a, ICudaEngineDoc::binding_is_input_str) - .def("get_binding_shape", &ICudaEngine::getBindingDimensions, "index"_a, ICudaEngineDoc::get_binding_shape) - // Overload so that we can get shape based on tensor names. - .def("get_binding_shape", lambdas::engine_get_binding_shape, "name"_a, ICudaEngineDoc::get_binding_shape_str) - .def("get_binding_dtype", &ICudaEngine::getBindingDataType, "index"_a, ICudaEngineDoc::get_binding_dtype) - // Overload so that we can get type based on tensor names. - .def("get_binding_dtype", lambdas::engine_get_binding_dtype, "name"_a, ICudaEngineDoc::get_binding_dtype_str) - .def_property_readonly("has_implicit_batch_dimension", &ICudaEngine::hasImplicitBatchDimension) - .def_property_readonly("max_batch_size", &ICudaEngine::getMaxBatchSize) - .def_property_readonly("num_layers", &ICudaEngine::getNbLayers) - .def_property_readonly("max_workspace_size", &ICudaEngine::getWorkspaceSize) - .def("serialize", &ICudaEngine::serialize, ICudaEngineDoc::serialize) - .def("create_execution_context", &ICudaEngine::createExecutionContext, ICudaEngineDoc::create_execution_context) - .def("get_location", &ICudaEngine::getLocation, "index"_a, ICudaEngineDoc::get_location) - .def("get_location", lambdas::engine_get_location, "name"_a, ICudaEngineDoc::get_location_str) - .def("create_execution_context_without_device_memory", &ICudaEngine::createExecutionContextWithoutDeviceMemory, ICudaEngineDoc::create_execution_context_without_device_memory) - .def_property_readonly("device_memory_size", &ICudaEngine::getDeviceMemorySize) - .def_property_readonly("refittable", &ICudaEngine::isRefittable) - .def_property_readonly("name", &ICudaEngine::getName) - .def_property_readonly("num_optimization_profiles", &ICudaEngine::getNbOptimizationProfiles) - .def("get_profile_shape", lambdas::engine_get_profile_shape, "profile_index"_a, "binding"_a, ICudaEngineDoc::get_profile_shape) - .def("get_profile_shape", lambdas::engine_get_profile_shape_str, "profile_index"_a, "binding"_a, ICudaEngineDoc::get_profile_shape) - .def("get_profile_shape_input", lambdas::engine_get_profile_shape_input, "profile_index"_a, "binding"_a, ICudaEngineDoc::get_profile_shape_input) - .def("get_profile_shape_input", lambdas::engine_get_profile_shape_input_str, "profile_index"_a, "binding"_a, ICudaEngineDoc::get_profile_shape_input) - .def("is_shape_binding", &ICudaEngine::isShapeBinding, "binding"_a, ICudaEngineDoc::is_shape_binding) - .def("is_execution_binding", &ICudaEngine::isExecutionBinding, "binding"_a, ICudaEngineDoc::is_execution_binding) - .def("get_binding_bytes_per_component", &ICudaEngine::getBindingBytesPerComponent, "index"_a, ICudaEngineDoc::get_binding_bytes_per_component) - .def("get_binding_components_per_element", &ICudaEngine::getBindingComponentsPerElement, "index"_a, ICudaEngineDoc::get_binding_components_per_element) - .def("get_binding_format", &ICudaEngine::getBindingFormat, "index"_a, ICudaEngineDoc::get_binding_format) - .def("get_binding_format_desc", &ICudaEngine::getBindingFormatDesc, "index"_a, ICudaEngineDoc::get_binding_format_desc) - .def("get_binding_vectorized_dim", &ICudaEngine::getBindingVectorizedDim, "index"_a, ICudaEngineDoc::get_binding_vectorized_dim) - .def("__del__", &ICudaEngine::destroy) - ; - - py::class_(m, "IGpuAllocator") - .def("allocate", &IGpuAllocator::allocate) - .def("free", &IGpuAllocator::free) - ; - - py::enum_(m, "BuilderFlag", py::arithmetic{}, BuilderFlagDoc::descr) - .value("FP16", BuilderFlag::kFP16, BuilderFlagDoc::FP16) - .value("INT8", BuilderFlag::kINT8, BuilderFlagDoc::INT8) - .value("DEBUG", BuilderFlag::kDEBUG, BuilderFlagDoc::DEBUG) - .value("GPU_FALLBACK", BuilderFlag::kGPU_FALLBACK, BuilderFlagDoc::GPU_FALLBACK) - .value("STRICT_TYPES", BuilderFlag::kSTRICT_TYPES, BuilderFlagDoc::STRICT_TYPES) - .value("REFIT", BuilderFlag::kREFIT, BuilderFlagDoc::REFIT) - .value("DISABLE_TIMING_CACHE", BuilderFlag::kDISABLE_TIMING_CACHE, BuilderFlagDoc::DISABLE_TIMING_CACHE) - .value("TF32", BuilderFlag::kTF32, BuilderFlagDoc::TF32); - - py::enum_(m, "QuantizationFlag", py::arithmetic{}, QuantizationFlagDoc::descr) - .value("CALIBRATE_BEFORE_FUSION", QuantizationFlag::kCALIBRATE_BEFORE_FUSION, - QuantizationFlagDoc::CALIBRATE_BEFORE_FUSION); - - py::enum_(m, "DeviceType", DeviceTypeDoc::descr) - .value("GPU", DeviceType::kGPU, DeviceTypeDoc::GPU) - .value("DLA", DeviceType::kDLA, DeviceTypeDoc::DLA); - - py::enum_(m, "ProfilingVerbosity", ProfilingVerbosityDoc::descr) - .value("DEFAULT", ProfilingVerbosity::kDEFAULT, ProfilingVerbosityDoc::DEFAULT) - .value("NONE", ProfilingVerbosity::kNONE, ProfilingVerbosityDoc::NONE) - .value("VERBOSE", ProfilingVerbosity::kVERBOSE, ProfilingVerbosityDoc::VERBOSE); - - py::enum_(m, "TacticSource", py::arithmetic{}) - .value("CUBLAS", TacticSource::kCUBLAS, TacticSourceDoc::CUBLAS) - .value("CUBLAS_LT", TacticSource::kCUBLAS_LT, TacticSourceDoc::CUBLAS_LT); - - py::class_>( - m, "IBuilderConfig", IBuilderConfigDoc::descr) - .def_property("min_timing_iterations", &IBuilderConfig::getMinTimingIterations, - &IBuilderConfig::setMinTimingIterations) - .def_property("avg_timing_iterations", &IBuilderConfig::getAvgTimingIterations, - &IBuilderConfig::setAvgTimingIterations) - .def_property("int8_calibrator", &IBuilderConfig::getInt8Calibrator, - py::cpp_function(&IBuilderConfig::setInt8Calibrator, py::keep_alive<1, 2>{})) - .def_property( - "max_workspace_size", &IBuilderConfig::getMaxWorkspaceSize, &IBuilderConfig::setMaxWorkspaceSize) - .def_property("flags", &IBuilderConfig::getFlags, &IBuilderConfig::setFlags) - .def_property( - "default_device_type", &IBuilderConfig::getDefaultDeviceType, &IBuilderConfig::setDefaultDeviceType) - .def_property("DLA_core", &IBuilderConfig::getDLACore, &IBuilderConfig::setDLACore) - .def("clear_flag", &IBuilderConfig::clearFlag, "flag"_a, IBuilderConfigDoc::clear_flag) - .def("set_flag", &IBuilderConfig::setFlag, "flag"_a, IBuilderConfigDoc::set_flag) - .def("get_flag", &IBuilderConfig::getFlag, "flag"_a, IBuilderConfigDoc::get_flag) - .def_property( - "quantization_flags", &IBuilderConfig::getQuantizationFlags, &IBuilderConfig::setQuantizationFlags) - .def("clear_quantization_flag", &IBuilderConfig::clearQuantizationFlag, "flag"_a, - IBuilderConfigDoc::clear_quantization_flag) - .def("set_quantization_flag", &IBuilderConfig::setQuantizationFlag, "flag"_a, - IBuilderConfigDoc::set_quantization_flag) - .def("get_quantization_flag", &IBuilderConfig::getQuantizationFlag, "flag"_a, - IBuilderConfigDoc::get_quantization_flag) - .def("reset", &IBuilderConfig::reset, IBuilderConfigDoc::reset) - .def_property( - "profile_stream", lambdas::netconfig_get_profile_stream, lambdas::netconfig_set_profile_stream) - .def("add_optimization_profile", &IBuilderConfig::addOptimizationProfile, "profile"_a, - IBuilderConfigDoc::add_optimization_profile) - .def("set_calibration_profile", &IBuilderConfig::setCalibrationProfile, "profile"_a, - IBuilderConfigDoc::set_calibration_profile) - .def("get_calibration_profile", &IBuilderConfig::getCalibrationProfile, - IBuilderConfigDoc::get_calibration_profile) - .def_property_readonly("num_optimization_profiles", &IBuilderConfig::getNbOptimizationProfiles) - .def("set_device_type", &IBuilderConfig::setDeviceType, "layer"_a, "device_type"_a, - IBuilderConfigDoc::set_device_type) - .def("get_device_type", &IBuilderConfig::getDeviceType, "layer"_a, IBuilderConfigDoc::get_device_type) - .def("is_device_type_set", &IBuilderConfig::isDeviceTypeSet, "layer"_a, - IBuilderConfigDoc::is_device_type_set) - .def("reset_device_type", &IBuilderConfig::resetDeviceType, "layer"_a, IBuilderConfigDoc::reset_device_type) - .def("can_run_on_DLA", &IBuilderConfig::canRunOnDLA, "layer"_a, IBuilderConfigDoc::can_run_on_DLA) - .def_property( - "profiling_verbosity", &IBuilderConfig::getProfilingVerbosity, &IBuilderConfig::setProfilingVerbosity) - .def_property( - "algorithm_selector", &IBuilderConfig::getAlgorithmSelector, &IBuilderConfig::setAlgorithmSelector) - .def("set_tactic_sources", &IBuilderConfig::setTacticSources, "tactic_sources"_a, - IBuilderConfigDoc::set_tactic_sources) - .def("get_tactic_sources", &IBuilderConfig::getTacticSources, - IBuilderConfigDoc::get_tactic_sources) - .def("__del__", &IBuilderConfig::destroy); - - py::enum_( - m, "NetworkDefinitionCreationFlag", py::arithmetic{}, NetworkDefinitionCreationFlagDoc::descr) - .value("EXPLICIT_BATCH", NetworkDefinitionCreationFlag::kEXPLICIT_BATCH, - NetworkDefinitionCreationFlagDoc::EXPLICIT_BATCH) - .value("EXPLICIT_PRECISION", NetworkDefinitionCreationFlag::kEXPLICIT_PRECISION, - NetworkDefinitionCreationFlagDoc::EXPLICIT_PRECISION); - - // Builder - py::class_>(m, "Builder", BuilderDoc::descr) - .def(py::init(&nvinfer1::createInferBuilder), "logger"_a, BuilderDoc::init) - .def("create_network", &IBuilder::createNetworkV2, "flags"_a = 0U, BuilderDoc::create_network) - .def_property("max_batch_size", &IBuilder::getMaxBatchSize, &IBuilder::setMaxBatchSize) - .def_property("max_workspace_size", &IBuilder::getMaxWorkspaceSize, &IBuilder::setMaxWorkspaceSize) - .def_property("debug_sync", &IBuilder::getDebugSync, &IBuilder::setDebugSync) - .def_property("min_find_iterations", &IBuilder::getMinFindIterations, &IBuilder::setMinFindIterations) - .def_property( - "average_find_iterations", &IBuilder::getAverageFindIterations, &IBuilder::setAverageFindIterations) - .def("build_cuda_engine", &IBuilder::buildCudaEngine, "network"_a, BuilderDoc::build_cuda_engine) - .def_property_readonly("platform_has_tf32", &IBuilder::platformHasTf32) - .def_property_readonly("platform_has_fast_fp16", &IBuilder::platformHasFastFp16) - .def_property_readonly("platform_has_fast_int8", &IBuilder::platformHasFastInt8) - .def_property("int8_mode", &IBuilder::getInt8Mode, &IBuilder::setInt8Mode) - .def_property( - "int8_calibrator", nullptr, py::cpp_function(&IBuilder::setInt8Calibrator, py::keep_alive<1, 2>{})) - .def_property("gpu_allocator", nullptr, &IBuilder::setGpuAllocator) - .def_property("fp16_mode", &IBuilder::getFp16Mode, &IBuilder::setFp16Mode) - .def_property( - "strict_type_constraints", &IBuilder::getStrictTypeConstraints, &IBuilder::setStrictTypeConstraints) - .def_property("refittable", &IBuilder::getRefittable, &IBuilder::setRefittable) - // Special return-value policy to ensure that Python does not take ownership of the returned pointer. - .def("create_optimization_profile", &IBuilder::createOptimizationProfile, - BuilderDoc::create_optimization_profile, py::return_value_policy::reference_internal) - .def_property("error_recorder", &IBuilder::getErrorRecorder, &IBuilder::setErrorRecorder) - .def("create_builder_config", &IBuilder::createBuilderConfig, BuilderDoc::create_builder_config) - .def("build_engine", &IBuilder::buildEngineWithConfig, "network"_a, "config"_a, BuilderDoc::build_engine) - .def("__del__", &IBuilder::destroy); - - // Runtime - py::class_>(m, "Runtime", RuntimeDoc::descr) - .def(py::init(&nvinfer1::createInferRuntime), "logger"_a, RuntimeDoc::init) - .def("deserialize_cuda_engine", lambdas::runtime_deserialize_cuda_engine, "serialized_engine"_a, - "plugin_factory"_a = nullptr, RuntimeDoc::deserialize_cuda_engine) - .def_property( - "gpu_allocator", nullptr, py::cpp_function(&IRuntime::setGpuAllocator, py::keep_alive<1, 2>{})) - .def("__del__", &IRuntime::destroy); - - // Refitter - py::class_>(m, "Refitter", RefitterDoc::descr) - .def(py::init(&nvinfer1::createInferRefitter), "engine"_a, "logger"_a, py::keep_alive<1, 2>{}, - RefitterDoc::init) - .def("set_weights", &IRefitter::setWeights, "layer_name"_a, "role"_a, "weights"_a, py::keep_alive<1, 4>{}, - RefitterDoc::set_weights) - .def("refit_cuda_engine", &IRefitter::refitCudaEngine, RefitterDoc::refit_cuda_engine) - .def("get_missing", lambdas::refitter_get_missing, RefitterDoc::get_missing) - .def("get_all", lambdas::refitter_get_all, RefitterDoc::get_all) - .def("get_dynamic_range", lambdas::refitter_get_dynamic_range, "tensor_name"_a, - RefitterDoc::get_dynamic_range) - .def("set_dynamic_range", lambdas::refitter_set_dynamic_range, "tensor_name"_a, "range"_a, - RefitterDoc::set_dynamic_range) - .def("get_tensors_with_dynamic_range", lambdas::refitter_get_tensors_with_dynamic_range, - RefitterDoc::get_tensors_with_dynamic_range) - .def("__del__", &IRefitter::destroy); + return nullptr; } - } // namespace tensorrt + void* allocate(uint64_t size, uint64_t alignment, AllocatorFlags flags) noexcept override + { + return allocHelper("allocate", true, size, alignment, flags); + } + + void* reallocate(void* baseAddr, uint64_t alignment, uint64_t newSize) noexcept override + { + return allocHelper("reallocate", false, reinterpret_cast(baseAddr), alignment, newSize); + } + + void free(void* memory) noexcept override + { + py::gil_scoped_acquire gil{}; + py::function pyFree = utils::getOverload(static_cast(this), "free"); + if (!pyFree) + { + return; + } + + try + { + pyFree(reinterpret_cast(memory)); + } + catch (...) + { + std::cerr << "[ERROR] Exception caught in free()" << std::endl; + } + } +}; + +void bindCore(py::module& m) +{ + class PyLogger : public ILogger + { + public: + virtual void log(Severity severity, const char* msg) noexcept override + { + PYBIND11_OVERLOAD_PURE_NAME(void, ILogger, "log", log, severity, msg); + } + }; + + py::class_(m, "ILogger", ILoggerDoc::descr) + .def(py::init<>()) + .def("log", &ILogger::log, "severity"_a, "msg"_a, ILoggerDoc::log); + ; + + class DefaultLogger : public ILogger + { + public: + DefaultLogger(Severity minSeverity = Severity::kWARNING) + : mMinSeverity(minSeverity) + { + } + + virtual void log(Severity severity, const char* msg) noexcept override + { + // INFO is the largest value, so this comparison is inverted. + if (severity > mMinSeverity) + return; + + std::string loggingPrefix = "[TensorRT] "; + if (severity == Severity::kINTERNAL_ERROR) + loggingPrefix += "INTERNAL ERROR: "; + else if (severity == Severity::kERROR) + loggingPrefix += "ERROR: "; + else if (severity == Severity::kWARNING) + loggingPrefix += "WARNING: "; + else if (severity == Severity::kINFO) + loggingPrefix += "INFO: "; + else if (severity == Severity::kVERBOSE) + loggingPrefix += "VERBOSE: "; + std::cerr << loggingPrefix << msg << std::endl; + } + + Severity mMinSeverity; + }; + + // Need to instantiate so we can put the Severity enum under DefaultLogger. + py::class_ loggerBinding(m, "Logger", LoggerDoc::descr); + + py::enum_(loggerBinding, "Severity", py::arithmetic()) + .value("INTERNAL_ERROR", ILogger::Severity::kINTERNAL_ERROR, SeverityDoc::internal_error) + .value("ERROR", ILogger::Severity::kERROR, SeverityDoc::error) + .value("WARNING", ILogger::Severity::kWARNING, SeverityDoc::warning) + .value("INFO", ILogger::Severity::kINFO, SeverityDoc::info) + .value("VERBOSE", ILogger::Severity::kVERBOSE, SeverityDoc::verbose) + // We export into the parent class, so we can access with trt.ILogger.X. + .export_values(); + + // Need to do this after, so that the severity enum is available. + loggerBinding.def(py::init(), "min_severity"_a = ILogger::Severity::kWARNING) + .def_readwrite("min_severity", &DefaultLogger::mMinSeverity) + .def("log", &DefaultLogger::log, "severity"_a, "msg"_a, LoggerDoc::log); + + class PyProfiler : public IProfiler + { + public: + void reportLayerTime(const char* layerName, float ms) noexcept override + { + PYBIND11_OVERLOAD_PURE_NAME(void, IProfiler, "report_layer_time", reportLayerTime, layerName, ms); + } + }; + + py::class_(m, "IProfiler", IProfilerDoc::descr) + .def(py::init<>()) + .def("report_layer_time", &IProfiler::reportLayerTime, "layer_name"_a, "ms"_a, IProfilerDoc::report_layer_time); + + class DefaultProfiler : public IProfiler + { + public: + void reportLayerTime(const char* layerName, float ms) noexcept override + { + std::cout << layerName << ": " << ms << "ms" << std::endl; + } + }; + + py::class_(m, "Profiler", ProfilerDoc::descr) + .def(py::init<>()) + .def("report_layer_time", &IProfiler::reportLayerTime, "layer_name"_a, "ms"_a, ProfilerDoc::report_layer_time); + + py::class_>( + m, "IOptimizationProfile", IOptimizationProfileDoc::descr) + .def("set_shape", lambdas::opt_profile_set_shape, "input"_a, "min"_a, "opt"_a, "max"_a, + IOptimizationProfileDoc::set_shape) + .def("get_shape", lambdas::opt_profile_get_shape, "input"_a, IOptimizationProfileDoc::get_shape) + .def("set_shape_input", lambdas::opt_profile_set_shape_input, "input"_a, "min"_a, "opt"_a, "max"_a, + IOptimizationProfileDoc::set_shape_input) + .def("get_shape_input", lambdas::opt_profile_get_shape_input, "input"_a, + IOptimizationProfileDoc::get_shape_input) + .def_property("extra_memory_target", &IOptimizationProfile::getExtraMemoryTarget, + &IOptimizationProfile::setExtraMemoryTarget) + .def("__nonzero__", &IOptimizationProfile::isValid) + .def("__bool__", &IOptimizationProfile::isValid); + + py::enum_(m, "ErrorCodeTRT", py::arithmetic{}, ErrorCodeDoc::descr) + .value("SUCCESS", ErrorCode::kSUCCESS, ErrorCodeDoc::SUCCESS) + .value("UNSPECIFIED_ERROR", ErrorCode::kUNSPECIFIED_ERROR, ErrorCodeDoc::UNSPECIFIED_ERROR) + .value("INTERNAL_ERROR", ErrorCode::kINTERNAL_ERROR, ErrorCodeDoc::INTERNAL_ERROR) + .value("INVALID_ARGUMENT", ErrorCode::kINVALID_ARGUMENT, ErrorCodeDoc::INVALID_ARGUMENT) + .value("INVALID_CONFIG", ErrorCode::kINVALID_CONFIG, ErrorCodeDoc::INVALID_CONFIG) + .value("FAILED_ALLOCATION", ErrorCode::kFAILED_ALLOCATION, ErrorCodeDoc::FAILED_ALLOCATION) + .value("FAILED_INITIALIZATION", ErrorCode::kFAILED_INITIALIZATION, ErrorCodeDoc::FAILED_INITIALIZATION) + .value("FAILED_EXECUTION", ErrorCode::kFAILED_EXECUTION, ErrorCodeDoc::FAILED_EXECUTION) + .value("FAILED_COMPUTATION", ErrorCode::kFAILED_COMPUTATION, ErrorCodeDoc::FAILED_COMPUTATION) + .value("INVALID_STATE", ErrorCode::kINVALID_STATE, ErrorCodeDoc::INVALID_STATE) + .value("UNSUPPORTED_STATE", ErrorCode::kUNSUPPORTED_STATE, ErrorCodeDoc::UNSUPPORTED_STATE); + + // Provide a base implementation of Error recorder. + // Trampoline class is required as this class needs to be implemented by user. + class PyErrorRecorder : public IErrorRecorder + { + public: + virtual ErrorCode getErrorCode(int32_t errorIdx) const noexcept override + { + PYBIND11_OVERLOAD_PURE_NAME(ErrorCode, IErrorRecorder, "get_error_code", getErrorCode, errorIdx); + } + + virtual ErrorDesc getErrorDesc(int32_t errorIdx) const noexcept override + { + PYBIND11_OVERLOAD_PURE_NAME(ErrorDesc, IErrorRecorder, "get_error_desc", getErrorDesc, errorIdx); + } + + virtual void clear() noexcept override + { + PYBIND11_OVERLOAD_PURE_NAME(void, IErrorRecorder, "clear", clear); + } + + virtual bool reportError(ErrorCode val, ErrorDesc desc) noexcept override + { + PYBIND11_OVERLOAD_PURE_NAME(bool, IErrorRecorder, "report_error", reportError, val, desc); + } + + virtual int32_t getNbErrors() const noexcept override + { + PYBIND11_OVERLOAD_PURE_NAME(int32_t, IErrorRecorder, "get_num_errors", getNbErrors); + } + + virtual bool hasOverflowed() const noexcept override + { + PYBIND11_OVERLOAD_PURE_NAME(bool, IErrorRecorder, "has_overflowed", hasOverflowed); + } + + virtual RefCount incRefCount() noexcept override + { + return ++mRefCount; + } + + virtual RefCount decRefCount() noexcept override + { + return --mRefCount; + } + + private: + int32_t mRefCount{0}; + }; + + py::class_(m, "IErrorRecorder", IErrorRecorderDoc::descr) + .def(py::init<>()) + .def_property_readonly("MAX_DESC_LENGTH", []() { return IErrorRecorder::kMAX_DESC_LENGTH; }) + .def("num_errors", &IErrorRecorder::getNbErrors, IErrorRecorderDoc::get_num_errors) + .def("get_error_code", &IErrorRecorder::getErrorCode, IErrorRecorderDoc::get_error_code) + .def("get_error_desc", &IErrorRecorder::getErrorDesc, IErrorRecorderDoc::get_error_desc) + .def("has_overflowed", &IErrorRecorder::hasOverflowed, IErrorRecorderDoc::has_overflowed) + .def("clear", &IErrorRecorder::clear, IErrorRecorderDoc::clear) + .def("report_error", &IErrorRecorder::reportError, IErrorRecorderDoc::report_error); + + py::class_(m, "IExecutionContext", IExecutionContextDoc::descr) + .def("execute", lambdas::execute, "batch_size"_a = 1, "bindings"_a, IExecutionContextDoc::execute, + py::call_guard{}) + .def("execute_async", lambdas::execute_async, "batch_size"_a = 1, "bindings"_a, "stream_handle"_a, + "input_consumed"_a = nullptr, IExecutionContextDoc::execute_async, py::call_guard{}) + .def("execute_v2", lambdas::execute_v2, "bindings"_a, IExecutionContextDoc::execute_v2, + py::call_guard{}) + .def("execute_async_v2", lambdas::execute_async_v2, "bindings"_a, "stream_handle"_a, + "input_consumed"_a = nullptr, IExecutionContextDoc::execute_async_v2, + py::call_guard{}) + .def_property("debug_sync", &IExecutionContext::getDebugSync, &IExecutionContext::setDebugSync) + .def_property("profiler", &IExecutionContext::getProfiler, + py::cpp_function(&IExecutionContext::setProfiler, py::keep_alive<1, 2>{})) + .def_property_readonly("engine", &IExecutionContext::getEngine) + .def_property( + "name", &IExecutionContext::getName, py::cpp_function(&IExecutionContext::setName, py::keep_alive<1, 2>{})) + // For writeonly properties, we use a nullptr getter. + // TODO: Does this make sense in Python? + .def_property("device_memory", nullptr, &IExecutionContext::setDeviceMemory) + .def_property("active_optimization_profile", &IExecutionContext::getOptimizationProfile, + utils::deprecate(lambdas::context_set_optimization_profile, "set_optimization_profile_async")) + .def("get_strides", &IExecutionContext::getStrides, "binding"_a, IExecutionContextDoc::get_strides) + .def("set_binding_shape", &IExecutionContext::setBindingDimensions, "binding"_a, "shape"_a, + IExecutionContextDoc::set_binding_shape) + .def("get_binding_shape", &IExecutionContext::getBindingDimensions, "binding"_a, + IExecutionContextDoc::get_binding_shape) + .def("set_shape_input", lambdas::context_set_shape_input, "binding"_a, "shape"_a, + IExecutionContextDoc::set_shape_input) + .def("get_shape", lambdas::context_get_shape, "binding"_a, IExecutionContextDoc::get_shape) + .def_property_readonly("all_binding_shapes_specified", &IExecutionContext::allInputDimensionsSpecified) + .def_property_readonly("all_shape_inputs_specified", &IExecutionContext::allInputShapesSpecified) + .def("set_optimization_profile_async", lambdas::context_set_optimization_profile_async, "profile_index"_a, + "stream_handle"_a, IExecutionContextDoc::set_optimization_profile_async, + py::call_guard{}) + .def_property("error_recorder", &IExecutionContext::getErrorRecorder, + py::cpp_function(&IExecutionContext::setErrorRecorder, py::keep_alive<1, 2>{})) + .def("__del__", &utils::doNothingDel); + + py::class_(m, "ICudaEngine", ICudaEngineDoc::descr) + .def_property_readonly("num_bindings", &ICudaEngine::getNbBindings) + .def("__len__", &ICudaEngine::getNbBindings) + .def("__getitem__", + [](ICudaEngine& self, const std::string& name) { return self.getBindingIndex(name.c_str()); }) + .def("__getitem__", lambdas::engine_getitem) + .def("get_binding_name", &ICudaEngine::getBindingName, "index"_a, ICudaEngineDoc::get_binding_name) + .def("get_binding_index", &ICudaEngine::getBindingIndex, "name"_a, ICudaEngineDoc::get_binding_index) + .def("binding_is_input", &ICudaEngine::bindingIsInput, "index"_a, ICudaEngineDoc::binding_is_input) + .def("binding_is_input", lambdas::engine_binding_is_input, "name"_a, ICudaEngineDoc::binding_is_input_str) + .def("get_binding_shape", &ICudaEngine::getBindingDimensions, "index"_a, ICudaEngineDoc::get_binding_shape) + // Overload so that we can get shape based on tensor names. + .def("get_binding_shape", lambdas::engine_get_binding_shape, "name"_a, ICudaEngineDoc::get_binding_shape_str) + .def("get_binding_dtype", &ICudaEngine::getBindingDataType, "index"_a, ICudaEngineDoc::get_binding_dtype) + // Overload so that we can get type based on tensor names. + .def("get_binding_dtype", lambdas::engine_get_binding_dtype, "name"_a, ICudaEngineDoc::get_binding_dtype_str) + .def_property_readonly("has_implicit_batch_dimension", &ICudaEngine::hasImplicitBatchDimension) + .def_property_readonly("max_batch_size", &ICudaEngine::getMaxBatchSize) + .def_property_readonly("num_layers", &ICudaEngine::getNbLayers) + .def("serialize", &ICudaEngine::serialize, ICudaEngineDoc::serialize) + .def("create_execution_context", &ICudaEngine::createExecutionContext, ICudaEngineDoc::create_execution_context, + py::keep_alive<0, 1>{}) + .def("get_location", &ICudaEngine::getLocation, "index"_a, ICudaEngineDoc::get_location) + .def("get_location", lambdas::engine_get_location, "name"_a, ICudaEngineDoc::get_location_str) + .def("create_execution_context_without_device_memory", &ICudaEngine::createExecutionContextWithoutDeviceMemory, + ICudaEngineDoc::create_execution_context_without_device_memory, py::keep_alive<0, 1>{}) + .def_property_readonly("device_memory_size", &ICudaEngine::getDeviceMemorySize) + .def_property_readonly("refittable", &ICudaEngine::isRefittable) + .def_property_readonly("name", &ICudaEngine::getName) + .def_property_readonly("num_optimization_profiles", &ICudaEngine::getNbOptimizationProfiles) + .def_property_readonly("engine_capability", &ICudaEngine::getEngineCapability) + .def("get_profile_shape", lambdas::engine_get_profile_shape, "profile_index"_a, "binding"_a, + ICudaEngineDoc::get_profile_shape) + .def("get_profile_shape", lambdas::engine_get_profile_shape_str, "profile_index"_a, "binding"_a, + ICudaEngineDoc::get_profile_shape) + .def("get_profile_shape_input", lambdas::engine_get_profile_shape_input, "profile_index"_a, "binding"_a, + ICudaEngineDoc::get_profile_shape_input) + .def("get_profile_shape_input", lambdas::engine_get_profile_shape_input_str, "profile_index"_a, "binding"_a, + ICudaEngineDoc::get_profile_shape_input) + .def("is_shape_binding", &ICudaEngine::isShapeBinding, "binding"_a, ICudaEngineDoc::is_shape_binding) + .def( + "is_execution_binding", &ICudaEngine::isExecutionBinding, "binding"_a, ICudaEngineDoc::is_execution_binding) + .def("get_binding_bytes_per_component", &ICudaEngine::getBindingBytesPerComponent, "index"_a, + ICudaEngineDoc::get_binding_bytes_per_component) + .def("get_binding_components_per_element", &ICudaEngine::getBindingComponentsPerElement, "index"_a, + ICudaEngineDoc::get_binding_components_per_element) + .def("get_binding_format", &ICudaEngine::getBindingFormat, "index"_a, ICudaEngineDoc::get_binding_format) + .def("get_binding_format_desc", &ICudaEngine::getBindingFormatDesc, "index"_a, + ICudaEngineDoc::get_binding_format_desc) + .def("get_binding_vectorized_dim", &ICudaEngine::getBindingVectorizedDim, "index"_a, + ICudaEngineDoc::get_binding_vectorized_dim) + .def_property("error_recorder", &ICudaEngine::getErrorRecorder, + py::cpp_function(&ICudaEngine::setErrorRecorder, py::keep_alive<1, 2>{})) + .def_property_readonly("tactic_sources", &ICudaEngine::getTacticSources) + .def("__del__", &utils::doNothingDel); + + py::enum_(m, "AllocatorFlag", py::arithmetic{}, AllocatorFlagDoc::descr) + .value("RESIZABLE", AllocatorFlag::kRESIZABLE, AllocatorFlagDoc::RESIZABLE); + + py::class_(m, "IGpuAllocator", GpuAllocatorDoc::descr) + .def(py::init<>()) + .def("allocate", &IGpuAllocator::allocate, "size"_a, "alignment"_a, "flags"_a, GpuAllocatorDoc::allocate) + .def("reallocate", &IGpuAllocator::reallocate, "address"_a, "alignment"_a, "new_size"_a, + GpuAllocatorDoc::reallocate) + .def("free", &IGpuAllocator::free, "memory"_a, GpuAllocatorDoc::free); + + py::enum_(m, "BuilderFlag", py::arithmetic{}, BuilderFlagDoc::descr) + .value("FP16", BuilderFlag::kFP16, BuilderFlagDoc::FP16) + .value("INT8", BuilderFlag::kINT8, BuilderFlagDoc::INT8) + .value("DEBUG", BuilderFlag::kDEBUG, BuilderFlagDoc::DEBUG) + .value("GPU_FALLBACK", BuilderFlag::kGPU_FALLBACK, BuilderFlagDoc::GPU_FALLBACK) + .value("STRICT_TYPES", BuilderFlag::kSTRICT_TYPES, BuilderFlagDoc::STRICT_TYPES) + .value("REFIT", BuilderFlag::kREFIT, BuilderFlagDoc::REFIT) + .value("DISABLE_TIMING_CACHE", BuilderFlag::kDISABLE_TIMING_CACHE, BuilderFlagDoc::DISABLE_TIMING_CACHE) + .value("TF32", BuilderFlag::kTF32, BuilderFlagDoc::TF32) + .value("SPARSE_WEIGHTS", BuilderFlag::kSPARSE_WEIGHTS, BuilderFlagDoc::SPARSE_WEIGHTS) + .value("SAFETY_SCOPE", BuilderFlag::kSAFETY_SCOPE, BuilderFlagDoc::SAFETY_SCOPE); + + py::enum_(m, "QuantizationFlag", py::arithmetic{}, QuantizationFlagDoc::descr) + .value("CALIBRATE_BEFORE_FUSION", QuantizationFlag::kCALIBRATE_BEFORE_FUSION, + QuantizationFlagDoc::CALIBRATE_BEFORE_FUSION); + + py::enum_(m, "DeviceType", DeviceTypeDoc::descr) + .value("GPU", DeviceType::kGPU, DeviceTypeDoc::GPU) + .value("DLA", DeviceType::kDLA, DeviceTypeDoc::DLA); + + // Bind to a Python enum called ProfilingVerbosity. + py::enum_(m, "ProfilingVerbosity", ProfilingVerbosityDoc::descr) + .value("DEFAULT", ProfilingVerbosity::kDEFAULT, ProfilingVerbosityDoc::DEFAULT) + .value("NONE", ProfilingVerbosity::kNONE, ProfilingVerbosityDoc::NONE) + .value("VERBOSE", ProfilingVerbosity::kVERBOSE, ProfilingVerbosityDoc::VERBOSE); + + py::enum_(m, "TacticSource", py::arithmetic{}, TacticSourceDoc::descr) + .value("CUBLAS", TacticSource::kCUBLAS, TacticSourceDoc::CUBLAS) + .value("CUBLAS_LT", TacticSource::kCUBLAS_LT, TacticSourceDoc::CUBLAS_LT) + .value("CUDNN", TacticSource::kCUDNN, TacticSourceDoc::CUDNN); + + py::enum_(m, "EngineCapability", py::arithmetic{}, EngineCapabilityDoc::descr) + .value("DEFAULT", EngineCapability::kDEFAULT, EngineCapabilityDoc::DEFAULT) + .value("SAFE_GPU", EngineCapability::kSAFE_GPU, EngineCapabilityDoc::SAFE_GPU) + .value("SAFE_DLA", EngineCapability::kSAFE_DLA, EngineCapabilityDoc::SAFE_DLA) + .value("STANDARD", EngineCapability::kSTANDARD, EngineCapabilityDoc::STANDARD) + .value("SAFETY", EngineCapability::kSAFETY, EngineCapabilityDoc::SAFETY) + .value("DLA_STANDALONE", EngineCapability::kDLA_STANDALONE, EngineCapabilityDoc::DLA_STANDALONE); + + py::class_(m, "ITimingCache", ITimingCacheDoc::descr) + .def("serialize", &ITimingCache::serialize, ITimingCacheDoc::serialize) + .def("combine", &ITimingCache::combine, "input_cache"_a, "ignore_mismatch"_a, ITimingCacheDoc::combine) + .def("reset", &ITimingCache::reset, ITimingCacheDoc::reset); + + py::class_(m, "IBuilderConfig", IBuilderConfigDoc::descr) + .def_property( + "min_timing_iterations", &IBuilderConfig::getMinTimingIterations, &IBuilderConfig::setMinTimingIterations) + .def_property( + "avg_timing_iterations", &IBuilderConfig::getAvgTimingIterations, &IBuilderConfig::setAvgTimingIterations) + .def_property("int8_calibrator", &IBuilderConfig::getInt8Calibrator, + py::cpp_function(&IBuilderConfig::setInt8Calibrator, py::keep_alive<1, 2>{})) + .def_property("engine_capability", &IBuilderConfig::getEngineCapability, &IBuilderConfig::setEngineCapability) + .def_property("max_workspace_size", &IBuilderConfig::getMaxWorkspaceSize, &IBuilderConfig::setMaxWorkspaceSize) + .def_property("flags", &IBuilderConfig::getFlags, &IBuilderConfig::setFlags) + .def_property( + "default_device_type", &IBuilderConfig::getDefaultDeviceType, &IBuilderConfig::setDefaultDeviceType) + .def_property("DLA_core", &IBuilderConfig::getDLACore, &IBuilderConfig::setDLACore) + .def("clear_flag", &IBuilderConfig::clearFlag, "flag"_a, IBuilderConfigDoc::clear_flag) + .def("set_flag", &IBuilderConfig::setFlag, "flag"_a, IBuilderConfigDoc::set_flag) + .def("get_flag", &IBuilderConfig::getFlag, "flag"_a, IBuilderConfigDoc::get_flag) + .def_property( + "quantization_flags", &IBuilderConfig::getQuantizationFlags, &IBuilderConfig::setQuantizationFlags) + .def("clear_quantization_flag", &IBuilderConfig::clearQuantizationFlag, "flag"_a, + IBuilderConfigDoc::clear_quantization_flag) + .def("set_quantization_flag", &IBuilderConfig::setQuantizationFlag, "flag"_a, + IBuilderConfigDoc::set_quantization_flag) + .def("get_quantization_flag", &IBuilderConfig::getQuantizationFlag, "flag"_a, + IBuilderConfigDoc::get_quantization_flag) + .def("reset", &IBuilderConfig::reset, IBuilderConfigDoc::reset) + .def_property("profile_stream", lambdas::netconfig_get_profile_stream, lambdas::netconfig_set_profile_stream) + .def("add_optimization_profile", &IBuilderConfig::addOptimizationProfile, "profile"_a, + IBuilderConfigDoc::add_optimization_profile) + .def("set_calibration_profile", &IBuilderConfig::setCalibrationProfile, "profile"_a, + IBuilderConfigDoc::set_calibration_profile) + .def("get_calibration_profile", &IBuilderConfig::getCalibrationProfile, + IBuilderConfigDoc::get_calibration_profile) + .def_property_readonly("num_optimization_profiles", &IBuilderConfig::getNbOptimizationProfiles) + .def("set_device_type", &IBuilderConfig::setDeviceType, "layer"_a, "device_type"_a, + IBuilderConfigDoc::set_device_type) + .def("get_device_type", &IBuilderConfig::getDeviceType, "layer"_a, IBuilderConfigDoc::get_device_type) + .def("is_device_type_set", &IBuilderConfig::isDeviceTypeSet, "layer"_a, IBuilderConfigDoc::is_device_type_set) + .def("reset_device_type", &IBuilderConfig::resetDeviceType, "layer"_a, IBuilderConfigDoc::reset_device_type) + .def("can_run_on_DLA", &IBuilderConfig::canRunOnDLA, "layer"_a, IBuilderConfigDoc::can_run_on_DLA) + .def_property( + "profiling_verbosity", &IBuilderConfig::getProfilingVerbosity, &IBuilderConfig::setProfilingVerbosity) + .def_property("algorithm_selector", &IBuilderConfig::getAlgorithmSelector, + py::cpp_function(&IBuilderConfig::setAlgorithmSelector, py::keep_alive<1, 2>{})) + .def("set_tactic_sources", &IBuilderConfig::setTacticSources, "tactic_sources"_a, + IBuilderConfigDoc::set_tactic_sources) + .def("get_tactic_sources", &IBuilderConfig::getTacticSources, IBuilderConfigDoc::get_tactic_sources) + .def("create_timing_cache", lambdas::netconfig_create_timing_cache, "serialized_timing_cache"_a, + IBuilderConfigDoc::create_timing_cache, py::call_guard{}) + .def("set_timing_cache", &IBuilderConfig::setTimingCache, "cache"_a, "ignore_mismatch"_a, + IBuilderConfigDoc::set_timing_cache, py::keep_alive<1, 2>{}) + .def("get_timing_cache", &IBuilderConfig::getTimingCache, IBuilderConfigDoc::get_timing_cache) + .def("__del__", &utils::doNothingDel); + + py::enum_( + m, "NetworkDefinitionCreationFlag", py::arithmetic{}, NetworkDefinitionCreationFlagDoc::descr) + .value("EXPLICIT_BATCH", NetworkDefinitionCreationFlag::kEXPLICIT_BATCH, + NetworkDefinitionCreationFlagDoc::EXPLICIT_BATCH) + .value("EXPLICIT_PRECISION", NetworkDefinitionCreationFlag::kEXPLICIT_PRECISION, + NetworkDefinitionCreationFlagDoc::EXPLICIT_PRECISION); + + // Builder + py::class_(m, "Builder", BuilderDoc::descr) + .def(py::init(&nvinfer1::createInferBuilder), "logger"_a, BuilderDoc::init, py::keep_alive<1, 2>{}) + .def("create_network", &IBuilder::createNetworkV2, "flags"_a = 0U, BuilderDoc::create_network, + py::keep_alive<0, 1>{}) + .def_property("max_batch_size", &IBuilder::getMaxBatchSize, &IBuilder::setMaxBatchSize) + .def_property_readonly("platform_has_tf32", &IBuilder::platformHasTf32) + .def_property_readonly("platform_has_fast_fp16", &IBuilder::platformHasFastFp16) + .def_property_readonly("platform_has_fast_int8", &IBuilder::platformHasFastInt8) + .def_property_readonly("max_DLA_batch_size", &IBuilder::getMaxDLABatchSize) + .def_property_readonly("num_DLA_cores", &IBuilder::getNbDLACores) + .def_property("gpu_allocator", nullptr, py::cpp_function(&IBuilder::setGpuAllocator, py::keep_alive<1, 2>{})) + .def("create_optimization_profile", &IBuilder::createOptimizationProfile, + BuilderDoc::create_optimization_profile, py::return_value_policy::reference_internal) + .def_property("error_recorder", &IBuilder::getErrorRecorder, + py::cpp_function(&IBuilder::setErrorRecorder, py::keep_alive<1, 2>{})) + .def("create_builder_config", &IBuilder::createBuilderConfig, BuilderDoc::create_builder_config, + py::keep_alive<0, 1>{}) + .def("build_engine", utils::deprecateMember(&IBuilder::buildEngineWithConfig, "build_serialized_network"), + "network"_a, "config"_a, BuilderDoc::build_engine, py::call_guard{}, + py::keep_alive<0, 1>{}) + .def("build_serialized_network", &IBuilder::buildSerializedNetwork, "network"_a, "config"_a, + BuilderDoc::build_serialized_network, py::call_guard{}) + .def("is_network_supported", &IBuilder::isNetworkSupported, "network"_a, "config"_a, + BuilderDoc::is_network_supported, py::call_guard{}) + .def("__del__", &utils::doNothingDel); + + // Runtime + py::class_(m, "Runtime", RuntimeDoc::descr) + .def(py::init(&nvinfer1::createInferRuntime), "logger"_a, RuntimeDoc::init, py::keep_alive<1, 2>{}) + .def("deserialize_cuda_engine", lambdas::runtime_deserialize_cuda_engine, "serialized_engine"_a, + RuntimeDoc::deserialize_cuda_engine, py::call_guard{}, py::keep_alive<0, 1>{}) + .def_property("DLA_core", &IRuntime::getDLACore, &IRuntime::setDLACore) + .def_property_readonly("num_DLA_cores", &IRuntime::getNbDLACores) + .def_property("gpu_allocator", nullptr, py::cpp_function(&IRuntime::setGpuAllocator, py::keep_alive<1, 2>{})) + .def_property("error_recorder", &IRuntime::getErrorRecorder, + py::cpp_function(&IRuntime::setErrorRecorder, py::keep_alive<1, 2>{})) + .def("__del__", &utils::doNothingDel); + + // Refitter + py::class_(m, "Refitter", RefitterDoc::descr) + .def(py::init(&nvinfer1::createInferRefitter), "engine"_a, "logger"_a, py::keep_alive<1, 2>{}, + py::keep_alive<1, 3>{}, RefitterDoc::init) + .def("set_weights", &IRefitter::setWeights, "layer_name"_a, "role"_a, "weights"_a, py::keep_alive<1, 4>{}, + RefitterDoc::set_weights) + .def("set_named_weights", &IRefitter::setNamedWeights, "name"_a, "weights"_a, py::keep_alive<1, 3>{}, + RefitterDoc::set_named_weights) + .def("refit_cuda_engine", &IRefitter::refitCudaEngine, RefitterDoc::refit_cuda_engine) + .def("get_missing", lambdas::refitter_get_missing, RefitterDoc::get_missing) + .def("get_missing_weights", lambdas::refitter_get_missing_weights, RefitterDoc::get_missing_weights) + .def("get_all", lambdas::refitter_get_all, RefitterDoc::get_all) + .def("get_all_weights", lambdas::refitter_get_all_weights, RefitterDoc::get_all_weights) + .def("get_dynamic_range", lambdas::refitter_get_dynamic_range, "tensor_name"_a, RefitterDoc::get_dynamic_range) + .def("set_dynamic_range", lambdas::refitter_set_dynamic_range, "tensor_name"_a, "range"_a, + RefitterDoc::set_dynamic_range) + .def("get_tensors_with_dynamic_range", lambdas::refitter_get_tensors_with_dynamic_range, + RefitterDoc::get_tensors_with_dynamic_range) + .def_property("error_recorder", &IRefitter::getErrorRecorder, + py::cpp_function(&IRefitter::setErrorRecorder, py::keep_alive<1, 2>{})) + .def("__del__", &utils::doNothingDel); +} + +} // namespace tensorrt diff --git a/python/src/infer/pyFoundationalTypes.cpp b/python/src/infer/pyFoundationalTypes.cpp index 9f0f7e84..8beb16b3 100644 --- a/python/src/infer/pyFoundationalTypes.cpp +++ b/python/src/infer/pyFoundationalTypes.cpp @@ -15,322 +15,283 @@ */ // This contains the fundamental types, i.e. Dims, Weights, dtype -#include "NvInfer.h" -#include "utils.h" -#include "infer/pyFoundationalTypesDoc.h" #include "ForwardDeclarations.h" -#include -// For vector support -#include -// For py::array +#include "utils.h" #include +#include + +#include "infer/pyFoundationalTypesDoc.h" +#include namespace tensorrt { - using namespace nvinfer1; +using namespace nvinfer1; - namespace lambdas +namespace lambdas +{ +// For Weights +static const auto weights_datatype_constructor = [](const DataType& type) { return new Weights{type, nullptr, 0}; }; + +static const auto weights_numpy_constructor = [](py::array& arr) { + // In order to construct a weights object, we must have a contiguous C-style array. + arr = py::array::ensure(arr, py::array::c_style); + if (!arr) { - // For Weights - static const auto weights_datatype_constructor = [] (const DataType& type) { - return new Weights{type, nullptr, 0}; - }; - - static const auto weights_numpy_constructor = [] (const py::array& buf) { - // Flags gives us numpy flags. py::array::c_style is an alias of numpy's C_CONTIGUOUS. - // In order to construct a weights object, we must have a contiguous array (C or F style). - bool isContiguous = buf.flags() & (py::array::c_style | py::array::f_style); - if (!isContiguous) throw std::runtime_error("Cannot construct Weights object from non-contiguous array. Please use numpy.ascontiguousarray."); - return new Weights{utils::type(buf.dtype()), buf.data(), buf.size()}; - }; - - // Helper to compare dims with any kind of Python Iterable. - template - bool dimsEqual(const DimsType& self, PyIterable& other) - { - if (other.size() != self.nbDims) return false; - bool eq = true; - std::vector o = other.template cast>(); - for (int i = 0; i < self.nbDims; ++i) - eq = eq && (self.d[i] == o[i]); - return eq; - } - - // For base Dims class - static const auto dims_vector_constructor = [] (const std::vector& in) { - // This is required, because otherwise MAX_DIMS will not be resolved at compile time. - const int maxDims = static_cast(Dims::MAX_DIMS); - if (in.size() > maxDims || in.size() < 0) - throw std::length_error("Input length " + std::to_string(in.size()) + ". Max expected length is " + std::to_string(maxDims)); - - // Create the Dims object. - Dims* self = new Dims{}; - self -> nbDims = in.size(); - for (int i = 0; i < in.size(); ++i) - self -> d[i] = in[i]; - return self; - }; - - static const auto dims_to_str = [] (const Dims& self) { - if (self.nbDims == 0) return std::string("()"); - // Length 1 should followed by trailing comma, for tuple-like behavior. - if (self.nbDims == 1) return "(" + std::to_string(self.d[0]) + ",)"; - // Non-zero lengths - std::string temp = "("; - for (int i = 0; i < self.nbDims - 1; ++i) - temp += std::to_string(self.d[i]) + ", "; - temp += std::to_string(self.d[self.nbDims - 1]) + ")"; - return temp; - }; - - static const auto dims_len = [] (const Dims& self) { return self.nbDims; }; - - static const auto dims_getter = [] (const Dims& self, int pyIndex) -> const int& { - // Without these bounds checks, horrible infinite looping will occur. - size_t index = (pyIndex < 0) ? static_cast(self.nbDims) + pyIndex : pyIndex; - if (index >= self.nbDims) throw py::index_error(); - return self.d[index]; - }; - - static const auto dims_getter_slice = [] (const Dims& self, py::slice slice) { - size_t start, stop, step, slicelength; - if (!slice.compute(self.nbDims, &start, &stop, &step, &slicelength)) - throw py::error_already_set(); - // Disallow out-of-bounds things. - if (stop > self.nbDims) throw py::index_error(); - - py::tuple ret{slicelength}; - for (int i = start, index = 0; i < stop; i += step, ++index) - ret[index] = self.d[i]; - return ret; - }; - - static const auto dims_setter = [] (Dims& self, int pyIndex, int item) { - size_t index = (pyIndex < 0) ? static_cast(self.nbDims) + pyIndex : pyIndex; - if (index >= self.nbDims) throw py::index_error(); - self.d[index] = item; - }; - - static const auto dims_setter_slice = [] (Dims& self, py::slice slice, const Dims& other) { - size_t start, stop, step, slicelength; - if (!slice.compute(self.nbDims, &start, &stop, &step, &slicelength)) - throw py::error_already_set(); - // Disallow out-of-bounds things. - if (stop >= self.nbDims) throw py::index_error(); - - for (int i = start, index = 0; i < stop; i += step, ++index) - self.d[i] = other.d[index]; - }; - - static const auto get_type = [] (const Dims& self, int pyIndex) { - size_t index = (pyIndex < 0) ? static_cast(self.nbDims) + pyIndex : pyIndex; - if (index >= self.nbDims) throw py::index_error(); - return self.type[index]; - }; - - // For Dims2 - static const auto dims2_vector_constructor = [] (const std::vector& in) { - if (in.size() != 2) throw std::length_error("Input length " + std::to_string(in.size()) + " not equal to expected Dims2 length, which is 2"); - return new Dims2{in[0], in[1]}; - }; - - // For DimsHW - static const auto dimshw_vector_constructor = [] (const std::vector& in) { - if (in.size() != 2) throw std::length_error("Input length " + std::to_string(in.size()) + " not equal to expected DimsHW length, which is 2"); - return new DimsHW{in[0], in[1]}; - }; - - // For Dims3 - static const auto dims3_vector_constructor = [] (const std::vector& in) { - if (in.size() != 3) throw std::length_error("Input length " + std::to_string(in.size()) + " not equal to expected Dims3 length, which is 3"); - return new Dims3{in[0], in[1], in[2]}; - }; - - // For DimsCHW - static const auto dimschw_vector_constructor = [] (const std::vector& in) { - if (in.size() != 3) throw std::length_error("Input length " + std::to_string(in.size()) + " not equal to expected DimsCHW length, which is 3"); - return new DimsCHW{in[0], in[1], in[2]}; - }; - - // For Dims4 - static const auto dims4_vector_constructor = [] (const std::vector& in) { - if (in.size() != 4) throw std::length_error("Input length " + std::to_string(in.size()) + " not equal to expected Dims4 length, which is 4"); - return new Dims4{in[0], in[1], in[2], in[3]}; - }; - - // For DimsNCHW - static const auto dimsnchw_vector_constructor = [] (const std::vector& in) { - if (in.size() != 4) throw std::length_error("Input length " + std::to_string(in.size()) + " not equal to expected DimsNCHW length, which is 4"); - return new DimsNCHW{in[0], in[1], in[2], in[3]}; - }; - - // For IHostMemory - static const auto host_memory_buffer_interface = [](IHostMemory& self) -> py::buffer_info { - return py::buffer_info( - self.data(), /* Pointer to buffer */ - utils::size(self.type()), /* Size of one scalar */ - py::format_descriptor::format(), /* Python struct-style format descriptor */ - 1, /* Number of dimensions */ - { self.size() }, /* Buffer dimensions */ - { utils::size(self.type()) } /* Strides (in bytes) for each index */ - ); - }; - } /* lambdas */ - - void bindFoundationalTypes(py::module& m) - { - // Bind the top level DataType enum. - py::enum_(m, "DataType", DataTypeDoc::descr) - .value("FLOAT", DataType::kFLOAT, DataTypeDoc::float32) - .value("HALF", DataType::kHALF, DataTypeDoc::float16) - .value("INT8", DataType::kINT8, DataTypeDoc::int8) - .value("INT32", DataType::kINT32, DataTypeDoc::int32) - .value("BOOL", DataType::kBOOL, DataTypeDoc::boolean) - ; // DataType - - // Also create direct mappings (so we can call trt.float32, for example). - m.attr("float32") = DataType::kFLOAT; - m.attr("float16") = DataType::kHALF; - m.attr("int8") = DataType::kINT8; - m.attr("int32") = DataType::kINT32; - m.attr("bool") = DataType::kBOOL; - - // Bind to a Python enum called DimensionType. - py::enum_(m, "DimensionType", DimensionTypeDoc::descr) - .value("SPATIAL", DimensionType::kSPATIAL, DimensionTypeDoc::SPATIAL) - .value("CHANNEL", DimensionType::kCHANNEL, DimensionTypeDoc::CHANNEL) - .value("INDEX", DimensionType::kINDEX, DimensionTypeDoc::INDEX) - .value("SEQUENCE", DimensionType::kSEQUENCE, DimensionTypeDoc::SEQUENCE) - ; // DimensionType - - py::enum_(m, "WeightsRole", WeightsRoleDoc::descr) - .value("KERNEL", WeightsRole::kKERNEL, WeightsRoleDoc::KERNEL) - .value("BIAS", WeightsRole::kBIAS, WeightsRoleDoc::BIAS) - .value("SHIFT", WeightsRole::kSHIFT, WeightsRoleDoc::SHIFT) - .value("SCALE", WeightsRole::kSCALE, WeightsRoleDoc::SCALE) - .value("CONSTANT", WeightsRole::kCONSTANT, WeightsRoleDoc::CONSTANT) - ; // WeightsRole - - // Weights - py::class_ (m, "Weights", WeightsDoc::descr) - // Can construct an empty weights object with type. Defaults to float32. - .def(py::init(lambdas::weights_datatype_constructor), "type"_a = DataType::kFLOAT, WeightsDoc::init_type) - // Allows for construction through any contiguous numpy array. It then keeps a pointer to that buffer (zero-copy). - .def(py::init(lambdas::weights_numpy_constructor), "a"_a, py::keep_alive<1, 2>(), WeightsDoc::init_numpy) - // Expose numpy-like attributes. - .def_property_readonly("dtype", [] (const Weights& self) -> DataType { return self.type; }) - .def_property_readonly("size", [] (const Weights& self) { return self.count; }) - .def_property_readonly("nbytes", [] (const Weights& self) { return utils::size(self.type) * self.count; }) - .def("numpy", utils::weights_to_numpy, py::return_value_policy::reference_internal, WeightsDoc::numpy) - .def("__len__", [] (const Weights& self) { return static_cast(self.count); }) - ; // Weights - - // Also allow implicit construction, so we can pass in numpy arrays instead of Weights. - py::implicitly_convertible(); - - // Dims - py::class_ (m, "Dims", DimsDoc::descr) - .def(py::init<>()) - // Allows for construction from python lists and tuples. - .def(py::init(lambdas::dims_vector_constructor), "shape"_a) - // static_cast is required here, or MAX_DIMS does not get pulled in until LOAD time. - .def_property_readonly("MAX_DIMS", [](const Dims& self){return static_cast(self.MAX_DIMS);}, DimsDoc::MAX_DIMS) - // Allow for string representations (displays like a python tuple). - .def("__str__", lambdas::dims_to_str) - .def("__repr__", lambdas::dims_to_str) - // Allow direct comparisons with tuples and lists. - .def("__eq__", lambdas::dimsEqual) - .def("__eq__", lambdas::dimsEqual) - // These functions allow us to use Dims like an iterable. - .def("__len__", lambdas::dims_len) - .def("__getitem__", lambdas::dims_getter) - .def("__getitem__", lambdas::dims_getter_slice) - .def("__setitem__", lambdas::dims_setter) - .def("__setitem__", lambdas::dims_setter_slice) - .def("get_type", lambdas::get_type, DimsDoc::get_type) - ; // Dims - - // Make it possible to use tuples/lists in Python in place of Dims. - py::implicitly_convertible, Dims>(); - - // 2D - py::class_ (m, "Dims2", Dims2Doc::descr) - .def(py::init<>()) - .def(py::init(), "dim0"_a, "dim1"_a) - // Allows for construction from a tuple/list. - .def(py::init(lambdas::dims2_vector_constructor), "shape"_a) - ; // Dims2 - - py::implicitly_convertible, Dims2>(); - - py::class_ (m, "DimsHW", DimsHWDoc::descr) - .def(py::init<>()) - .def(py::init(), "h"_a, "w"_a) - // Allows for construction from a tuple/list. - .def(py::init(lambdas::dimshw_vector_constructor), "shape"_a) - // Expose these functions as attributes in Python. - .def_property("h", [] (const DimsHW& dims) { return dims.h(); }, [] (DimsHW& dims, int i) { dims.h() = i; }) - .def_property("w", [] (const DimsHW& dims) { return dims.w(); }, [] (DimsHW& dims, int i) { dims.w() = i; }) - ; // DimsHW - - py::implicitly_convertible, DimsHW>(); - - // 3D - py::class_ (m, "Dims3", Dims3Doc::descr) - .def(py::init<>()) - .def(py::init(), "dim0"_a, "dim1"_a, "dim2"_a) - // Allows for construction from a tuple/list. - .def(py::init(lambdas::dims3_vector_constructor), "shape"_a) - ; // Dims3 - - py::implicitly_convertible, Dims3>(); - - py::class_ (m, "DimsCHW", DimsCHWDoc::descr) - .def(py::init<>()) - .def(py::init(), "c"_a, "h"_a, "w"_a) - // Allows for construction from a tuple/list. - .def(py::init(lambdas::dimschw_vector_constructor), "shape"_a) - // Expose these functions as attributes in Python. - .def_property("c", [] (const DimsCHW& dims) { return dims.c(); }, [] (DimsCHW& dims, int i) { dims.c() = i; }) - .def_property("h", [] (const DimsCHW& dims) { return dims.h(); }, [] (DimsCHW& dims, int i) { dims.h() = i; }) - .def_property("w", [] (const DimsCHW& dims) { return dims.w(); }, [] (DimsCHW& dims, int i) { dims.w() = i; }) - ; // DimsCHW - - py::implicitly_convertible, DimsCHW>(); - - // 4D - py::class_ (m, "Dims4", Dims4Doc::descr) - .def(py::init<>()) - .def(py::init(), "dim0"_a, "dim1"_a, "dim2"_a, "dim3"_a) - // Allows for construction from a tuple/list. - .def(py::init(lambdas::dims4_vector_constructor), "shape"_a) - ; // Dims4 - - py::implicitly_convertible, Dims4>(); - - py::class_ (m, "DimsNCHW", DimsNCHWDoc::descr) - .def(py::init<>()) - .def(py::init(), "n"_a, "c"_a, "h"_a, "w"_a) - // Allows for construction from a tuple/list. - .def(py::init(lambdas::dimsnchw_vector_constructor), "shape"_a) - // Expose these functions as attributes in Python. - .def_property("n", [] (const DimsNCHW& dims) { return dims.n(); }, [] (DimsNCHW& dims, int i) { dims.n() = i; }) - .def_property("c", [] (const DimsNCHW& dims) { return dims.c(); }, [] (DimsNCHW& dims, int i) { dims.c() = i; }) - .def_property("h", [] (const DimsNCHW& dims) { return dims.h(); }, [] (DimsNCHW& dims, int i) { dims.h() = i; }) - .def_property("w", [] (const DimsNCHW& dims) { return dims.w(); }, [] (DimsNCHW& dims, int i) { dims.w() = i; }) - ; // DimsNCHW - - py::implicitly_convertible, DimsNCHW>(); - - // This class has a protected destructor, so we have to let pybind know (py::nodelete). - py::class_> (m, "IHostMemory", py::buffer_protocol(), IHostMemoryDoc::descr) - .def_property_readonly("dtype", [] (const IHostMemory& mem) { return mem.type(); }) - .def_property_readonly("nbytes", [] (const IHostMemory& mem) { return mem.size(); }) - // Expose buffer interface. - .def_buffer(lambdas::host_memory_buffer_interface) - .def("__del__", &IHostMemory::destroy) - ; // IHostMemory - + constexpr const char* err + = "Cannot construct Weights object from non-contiguous array. Please use numpy.ascontiguousarray() " + "to fix this."; + std::cout << "[ERROR] " << err << std::endl; + throw std::invalid_argument{err}; } + return new Weights{utils::type(arr.dtype()), arr.data(), arr.size()}; +}; -} /* tensorrt */ +// Helper to compare dims with any kind of Python Iterable. +template +bool dimsEqual(const DimsType& self, PyIterable& other) +{ + if (other.size() != self.nbDims) + { + return false; + } + bool eq = true; + std::vector o = other.template cast>(); + for (int i = 0; i < self.nbDims; ++i) + { + eq = eq && (self.d[i] == o[i]); + } + return eq; +} + +// For base Dims class +static const auto dims_vector_constructor = [](const std::vector& in) { + // This is required, because otherwise MAX_DIMS will not be resolved at compile time. + const int maxDims = static_cast(Dims::MAX_DIMS); + if (in.size() > maxDims || in.size() < 0) + throw std::length_error( + "Input length " + std::to_string(in.size()) + ". Max expected length is " + std::to_string(maxDims)); + + // Create the Dims object. + Dims* self = new Dims{}; + self->nbDims = in.size(); + for (int i = 0; i < in.size(); ++i) + self->d[i] = in[i]; + return self; +}; + +static const auto dims_to_str = [](const Dims& self) { + if (self.nbDims == 0) + return std::string("()"); + // Length 1 should followed by trailing comma, for tuple-like behavior. + if (self.nbDims == 1) + return "(" + std::to_string(self.d[0]) + ",)"; + // Non-zero lengths + std::string temp = "("; + for (int i = 0; i < self.nbDims - 1; ++i) + temp += std::to_string(self.d[i]) + ", "; + temp += std::to_string(self.d[self.nbDims - 1]) + ")"; + return temp; +}; + +static const auto dims_len = [](const Dims& self) { return self.nbDims; }; + +// TODO: Add slicing support? +static const auto dims_getter = [](const Dims& self, int pyIndex) -> const int& { + // Without these bounds checks, horrible infinite looping will occur. + size_t index = (pyIndex < 0) ? static_cast(self.nbDims) + pyIndex : pyIndex; + if (index >= self.nbDims) + throw py::index_error(); + return self.d[index]; +}; + +static const auto dims_getter_slice = [](const Dims& self, py::slice slice) { + size_t start, stop, step, slicelength; + if (!slice.compute(self.nbDims, &start, &stop, &step, &slicelength)) + throw py::error_already_set(); + // Disallow out-of-bounds things. + if (stop > self.nbDims) + throw py::index_error(); + + py::tuple ret{slicelength}; + for (int i = start, index = 0; i < stop; i += step, ++index) + ret[index] = self.d[i]; + return ret; +}; + +static const auto dims_setter = [](Dims& self, int pyIndex, int item) { + size_t index = (pyIndex < 0) ? static_cast(self.nbDims) + pyIndex : pyIndex; + if (index >= self.nbDims) + throw py::index_error(); + self.d[index] = item; +}; + +static const auto dims_setter_slice = [](Dims& self, py::slice slice, const Dims& other) { + size_t start, stop, step, slicelength; + if (!slice.compute(self.nbDims, &start, &stop, &step, &slicelength)) + throw py::error_already_set(); + // Disallow out-of-bounds things. + if (stop >= self.nbDims) + throw py::index_error(); + + for (int i = start, index = 0; i < stop; i += step, ++index) + self.d[i] = other.d[index]; +}; + +// For Dims2 +static const auto dims2_vector_constructor = [](const std::vector& in) { + if (in.size() != 2) + throw std::length_error( + "Input length " + std::to_string(in.size()) + " not equal to expected Dims2 length, which is 2"); + return new Dims2{in[0], in[1]}; +}; + +// For DimsHW +static const auto dimshw_vector_constructor = [](const std::vector& in) { + if (in.size() != 2) + throw std::length_error( + "Input length " + std::to_string(in.size()) + " not equal to expected DimsHW length, which is 2"); + return new DimsHW{in[0], in[1]}; +}; + +// For Dims3 +static const auto dims3_vector_constructor = [](const std::vector& in) { + if (in.size() != 3) + throw std::length_error( + "Input length " + std::to_string(in.size()) + " not equal to expected Dims3 length, which is 3"); + return new Dims3{in[0], in[1], in[2]}; +}; + +// For Dims4 +static const auto dims4_vector_constructor = [](const std::vector& in) { + if (in.size() != 4) + throw std::length_error( + "Input length " + std::to_string(in.size()) + " not equal to expected Dims4 length, which is 4"); + return new Dims4{in[0], in[1], in[2], in[3]}; +}; + +// For IHostMemory +static const auto host_memory_buffer_interface = [](IHostMemory& self) -> py::buffer_info { + return py::buffer_info(self.data(), /* Pointer to buffer */ + utils::size(self.type()), /* Size of one scalar */ + py::format_descriptor::format(), /* Python struct-style format descriptor */ + 1, /* Number of dimensions */ + {self.size()}, /* Buffer dimensions */ + {utils::size(self.type())} /* Strides (in bytes) for each index */ + ); +}; +} // namespace lambdas + +void bindFoundationalTypes(py::module& m) +{ + // Bind the top level DataType enum. + py::enum_(m, "DataType", DataTypeDoc::descr) + .value("FLOAT", DataType::kFLOAT, DataTypeDoc::float32) + .value("HALF", DataType::kHALF, DataTypeDoc::float16) + .value("INT8", DataType::kINT8, DataTypeDoc::int8) + .value("INT32", DataType::kINT32, DataTypeDoc::int32) + .value("BOOL", DataType::kBOOL, DataTypeDoc::boolean); // DataType + + // Also create direct mappings (so we can call trt.float32, for example). + m.attr("float32") = DataType::kFLOAT; + m.attr("float16") = DataType::kHALF; + m.attr("int8") = DataType::kINT8; + m.attr("int32") = DataType::kINT32; + m.attr("bool") = DataType::kBOOL; + + py::enum_(m, "WeightsRole", WeightsRoleDoc::descr) + .value("KERNEL", WeightsRole::kKERNEL, WeightsRoleDoc::KERNEL) + .value("BIAS", WeightsRole::kBIAS, WeightsRoleDoc::BIAS) + .value("SHIFT", WeightsRole::kSHIFT, WeightsRoleDoc::SHIFT) + .value("SCALE", WeightsRole::kSCALE, WeightsRoleDoc::SCALE) + .value("CONSTANT", WeightsRole::kCONSTANT, WeightsRoleDoc::CONSTANT) + .value("ANY", WeightsRole::kANY, WeightsRoleDoc::ANY); // WeightsRole + + // Weights + py::class_(m, "Weights", WeightsDoc::descr) + // Can construct an empty weights object with type. Defaults to float32. + .def(py::init(lambdas::weights_datatype_constructor), "type"_a = DataType::kFLOAT, WeightsDoc::init_type) + // Allows for construction through any contiguous numpy array. It then keeps a pointer to that buffer + // (zero-copy). + .def(py::init(lambdas::weights_numpy_constructor), "a"_a, py::keep_alive<1, 2>(), WeightsDoc::init_numpy) + // Expose numpy-like attributes. + .def_property_readonly("dtype", [](const Weights& self) -> DataType { return self.type; }) + .def_property_readonly("size", [](const Weights& self) { return self.count; }) + .def_property_readonly("nbytes", [](const Weights& self) { return utils::size(self.type) * self.count; }) + .def("numpy", utils::weights_to_numpy, py::return_value_policy::reference_internal, WeightsDoc::numpy) + .def("__len__", [](const Weights& self) { return static_cast(self.count); }); // Weights + + // Also allow implicit construction, so we can pass in numpy arrays instead of Weights. + py::implicitly_convertible(); + + // Dims + py::class_(m, "Dims", DimsDoc::descr) + .def(py::init<>()) + // Allows for construction from python lists and tuples. + .def(py::init(lambdas::dims_vector_constructor), "shape"_a) + // static_cast is required here, or MAX_DIMS does not get pulled in until LOAD time. + .def_property_readonly( + "MAX_DIMS", [](const Dims& self) { return static_cast(self.MAX_DIMS); }, DimsDoc::MAX_DIMS) + // Allow for string representations (displays like a python tuple). + .def("__str__", lambdas::dims_to_str) + .def("__repr__", lambdas::dims_to_str) + // Allow direct comparisons with tuples and lists. + .def("__eq__", lambdas::dimsEqual) + .def("__eq__", lambdas::dimsEqual) + // These functions allow us to use Dims like an iterable. + .def("__len__", lambdas::dims_len) + .def("__getitem__", lambdas::dims_getter) + .def("__getitem__", lambdas::dims_getter_slice) + .def("__setitem__", lambdas::dims_setter) + .def("__setitem__", lambdas::dims_setter_slice); // Dims + + // Make it possible to use tuples/lists in Python in place of Dims. + py::implicitly_convertible, Dims>(); + + // 2D + py::class_(m, "Dims2", Dims2Doc::descr) + .def(py::init<>()) + .def(py::init(), "dim0"_a, "dim1"_a) + // Allows for construction from a tuple/list. + .def(py::init(lambdas::dims2_vector_constructor), "shape"_a); // Dims2 + + py::implicitly_convertible, Dims2>(); + + py::class_(m, "DimsHW", DimsHWDoc::descr) + .def(py::init<>()) + .def(py::init(), "h"_a, "w"_a) + // Allows for construction from a tuple/list. + .def(py::init(lambdas::dimshw_vector_constructor), "shape"_a) + // Expose these functions as attributes in Python. + .def_property("h", [](const DimsHW& dims) { return dims.h(); }, [](DimsHW& dims, int i) { dims.h() = i; }) + .def_property( + "w", [](const DimsHW& dims) { return dims.w(); }, [](DimsHW& dims, int i) { dims.w() = i; }); // DimsHW + + py::implicitly_convertible, DimsHW>(); + + // 3D + py::class_(m, "Dims3", Dims3Doc::descr) + .def(py::init<>()) + .def(py::init(), "dim0"_a, "dim1"_a, "dim2"_a) + // Allows for construction from a tuple/list. + .def(py::init(lambdas::dims3_vector_constructor), "shape"_a); // Dims3 + + py::implicitly_convertible, Dims3>(); + + // 4D + py::class_(m, "Dims4", Dims4Doc::descr) + .def(py::init<>()) + .def(py::init(), "dim0"_a, "dim1"_a, "dim2"_a, "dim3"_a) + // Allows for construction from a tuple/list. + .def(py::init(lambdas::dims4_vector_constructor), "shape"_a); // Dims4 + + py::implicitly_convertible, Dims4>(); + + py::class_(m, "IHostMemory", py::buffer_protocol(), IHostMemoryDoc::descr) + .def_property_readonly("dtype", [](const IHostMemory& mem) { return mem.type(); }) + .def_property_readonly("nbytes", [](const IHostMemory& mem) { return mem.size(); }) + // Expose buffer interface. + .def_buffer(lambdas::host_memory_buffer_interface) + .def("__del__", &utils::doNothingDel); // IHostMemory +} + +} // namespace tensorrt diff --git a/python/src/infer/pyGraph.cpp b/python/src/infer/pyGraph.cpp index 77d25548..40d7dc44 100644 --- a/python/src/infer/pyGraph.cpp +++ b/python/src/infer/pyGraph.cpp @@ -15,14 +15,12 @@ */ // This file contains all bindings related to TensorRT INetworkDefinition. -#include "NvInfer.h" - -#include "infer/pyGraphDoc.h" #include "ForwardDeclarations.h" #include "utils.h" -// For vector support #include +#include "infer/pyGraphDoc.h" + // clang-format off namespace tensorrt { @@ -58,7 +56,7 @@ namespace tensorrt // For permutation static const auto permutation_vector_constructor = [] (const std::vector& in) { // Static casts are required here, so that MAX_DIMS is resolved at compile/link time. - const int maxDims = static_cast(nvinfer1::Dims::MAX_DIMS); + const int maxDims = static_cast(Dims::MAX_DIMS); if (in.size() > maxDims || in.size() < 0) throw std::length_error("Invalid input length. Max expected length is " + std::to_string(maxDims)); Permutation* self = new Permutation{}; @@ -68,7 +66,7 @@ namespace tensorrt }; static const auto permutation_to_str = [] (const Permutation& self) { - const int maxDims = static_cast(nvinfer1::Dims::MAX_DIMS); + const int maxDims = static_cast(Dims::MAX_DIMS); std::string temp = "("; for (int i = 0; i < maxDims - 1; ++i) temp += std::to_string(self.order[i]) + ", "; @@ -76,46 +74,37 @@ namespace tensorrt return temp; }; + // TODO: Add slicing support? static const auto permutation_getter = [] (const Permutation& self, int pyIndex) { - size_t index = (pyIndex < 0) ? static_cast(nvinfer1::Dims::MAX_DIMS) + pyIndex : pyIndex; + size_t index = (pyIndex < 0) ? static_cast(Dims::MAX_DIMS) + pyIndex : pyIndex; // Static cast is REQUIRED here, or chaos ensues as MAX_DIMS is not pulled in at link time. - if (index >= static_cast(nvinfer1::Dims::MAX_DIMS)) throw py::index_error(); + if (index >= static_cast(Dims::MAX_DIMS)) throw py::index_error(); return self.order[index]; }; static const auto permutation_setter = [] (Permutation& self, int pyIndex, int item) { - size_t index = (pyIndex < 0) ? static_cast(nvinfer1::Dims::MAX_DIMS) + pyIndex : pyIndex; + size_t index = (pyIndex < 0) ? static_cast(Dims::MAX_DIMS) + pyIndex : pyIndex; // Static cast is REQUIRED here, or chaos ensues as MAX_DIMS is not pulled in at link time. - if (index >= static_cast(nvinfer1::Dims::MAX_DIMS)) throw py::index_error(); + if (index >= static_cast(Dims::MAX_DIMS)) throw py::index_error(); self.order[index] = item; }; static const auto permutation_len = [] (const Permutation& self) { - return static_cast(nvinfer1::Dims::MAX_DIMS); + return static_cast(Dims::MAX_DIMS); }; // For INetworkDefinition // Need a ptr to const-ptr to ITensor. - static const auto add_concatenation = [] (INetworkDefinition& self, const std::vector& inputs) { + static const auto add_concatenation = [] (INetworkDefinition& self, const std::vector& inputs) { return self.addConcatenation(inputs.data(), inputs.size()); }; // Need a ptr to const-ptr to ITensor. - static const auto add_plugin = [] (INetworkDefinition& self, const std::vector& inputs, nvinfer1::IPlugin& plugin) { - return self.addPlugin(inputs.data(), inputs.size(), plugin); - }; - - // Need a ptr to const-ptr to ITensor. - static const auto add_plugin_ext = [] (INetworkDefinition& self, const std::vector& inputs, nvinfer1::IPluginExt& plugin) { - return self.addPluginExt(inputs.data(), inputs.size(), plugin); - }; - - // Need a ptr to const-ptr to ITensor. - static const auto add_plugin_v2 = [] (INetworkDefinition& self, const std::vector& inputs, nvinfer1::IPluginV2& plugin) { + static const auto add_plugin_v2 = [] (INetworkDefinition& self, const std::vector& inputs, IPluginV2& plugin) { return self.addPluginV2(inputs.data(), inputs.size(), plugin); }; - static const auto add_convolution = [](INetworkDefinition& self, ITensor& input, int numOutputMaps, DimsHW kernelSize, Weights kernel, Weights* bias) + IConvolutionLayer* add_convolution(INetworkDefinition& self, ITensor& input, int numOutputMaps, DimsHW kernelSize, Weights kernel, Weights* bias) { return self.addConvolution(input, numOutputMaps, kernelSize, kernel, optionalWeights(bias)); }; @@ -140,7 +129,17 @@ namespace tensorrt return self.addScaleNd(input, mode, optionalWeights(shift), optionalWeights(scale), optionalWeights(power), channelAxis); }; - static const auto add_deconvolution = [](INetworkDefinition& self, ITensor& input, int numOutputMaps, DimsHW kernelSize, Weights kernel, Weights* bias) + static const auto add_quantize = [](INetworkDefinition& self, ITensor& input, ITensor& scale) + { + return self.addQuantize(input, scale); + }; + + static const auto add_dequantize = [](INetworkDefinition& self, ITensor& input, ITensor& scale) + { + return self.addDequantize(input, scale); + }; + + IDeconvolutionLayer* add_deconvolution(INetworkDefinition& self, ITensor& input, int numOutputMaps, DimsHW kernelSize, Weights kernel, Weights* bias) { return self.addDeconvolution(input, numOutputMaps, kernelSize, kernel, optionalWeights(bias)); }; @@ -150,6 +149,7 @@ namespace tensorrt return self.addDeconvolutionNd(input, numOutputMaps, kernelSize, kernel, optionalWeights(bias)); }; + // TODO: Need to ensure that these are returning by reference rather than by copy. // NumPy getters for layers. static const auto conv_get_kernel = [](IConvolutionLayer& self) { auto w = self.getKernelWeights(); return utils::weights_to_numpy(w); }; static const auto conv_get_bias = [](IConvolutionLayer& self) { auto w = self.getBiasWeights(); return utils::weights_to_numpy(w); }; @@ -164,9 +164,6 @@ namespace tensorrt static const auto deconv_get_kernel = [](IDeconvolutionLayer& self) { auto w = self.getKernelWeights(); return utils::weights_to_numpy(w); }; static const auto deconv_get_bias = [](IDeconvolutionLayer& self) { auto w = self.getBiasWeights(); return utils::weights_to_numpy(w); }; - static const auto rnn_get_weights = [](IRNNLayer& self) { auto w = self.getWeights(); return utils::weights_to_numpy(w); }; - static const auto rnn_get_bias = [](IRNNLayer& self) { auto w = self.getBias(); return utils::weights_to_numpy(w); }; - static const auto rnnv2_get_weights = [](IRNNv2Layer& self, int index, RNNGateType gate, bool isW) { auto w = self.getWeightsForGate(index, gate, isW); return utils::weights_to_numpy(w); }; @@ -176,6 +173,7 @@ namespace tensorrt static const auto constant_get_weights = [](IConstantLayer& self) { auto w = self.getWeights(); return utils::weights_to_numpy(w); }; + // TODO: Add slicing support? static const auto network_getitem = [](INetworkDefinition& self, int pyIndex) { // Support python's negative indexing size_t index = (pyIndex < 0) ? self.getNbLayers() + pyIndex : pyIndex; @@ -214,7 +212,6 @@ namespace tensorrt .value("CONCATENATION", LayerType::kCONCATENATION, LayerTypeDoc::CONCATENATION) .value("ELEMENTWISE", LayerType::kELEMENTWISE, LayerTypeDoc::ELEMENTWISE) .value("PLUGIN", LayerType::kPLUGIN, LayerTypeDoc::PLUGIN) - .value("RNN", LayerType::kRNN, LayerTypeDoc::RNN) .value("UNARY", LayerType::kUNARY, LayerTypeDoc::UNARY) .value("PADDING", LayerType::kPADDING, LayerTypeDoc::PADDING) .value("SHUFFLE", LayerType::kSHUFFLE, LayerTypeDoc::SHUFFLE) @@ -237,6 +234,8 @@ namespace tensorrt .value("LOOP_OUTPUT", LayerType::kLOOP_OUTPUT, LayerTypeDoc::LOOP_OUTPUT) .value("SELECT", LayerType::kSELECT, LayerTypeDoc::SELECT) .value("FILL", LayerType::kFILL, LayerTypeDoc::FILL) + .value("QUANTIZE", LayerType::kQUANTIZE, LayerTypeDoc::QUANTIZE) + .value("DEQUANTIZE", LayerType::kDEQUANTIZE, LayerTypeDoc::DEQUANTIZE) ; // LayerType // Bind to a Python enum called TensorLocation. @@ -257,6 +256,7 @@ namespace tensorrt .value("HWC", TensorFormat::kHWC, TensorFormatDoc::HWC) .value("DLA_LINEAR", TensorFormat::kDLA_LINEAR, TensorFormatDoc::DLA_LINEAR) .value("DLA_HWC4", TensorFormat::kDLA_HWC4, TensorFormatDoc::DLA_HWC4) + .value("HWC16", TensorFormat::kHWC16, TensorFormatDoc::HWC16) ; // TensorFormat // ITensor @@ -274,7 +274,6 @@ namespace tensorrt .def_property("dynamic_range", lambdas::get_dynamic_range, lambdas::set_dynamic_range) .def_property("allowed_formats", &ITensor::getAllowedFormats, &ITensor::setAllowedFormats) .def("set_dynamic_range", &ITensor::setDynamicRange, "min"_a, "max"_a, ITensorDoc::set_dynamic_range) - .def("get_dynamic_range", &ITensor::getDynamicRange, ITensorDoc::get_dynamic_range) .def("reset_dynamic_range", &ITensor::resetDynamicRange, ITensorDoc::reset_dynamic_range) ; @@ -305,10 +304,10 @@ namespace tensorrt ; py::class_>(m, "IConvolutionLayer", IConvolutionLayerDoc::descr) - .def_property("kernel_size", &IConvolutionLayer::getKernelSize, &IConvolutionLayer::setKernelSize) + .def_property("kernel_size", utils::deprecateMember(&IConvolutionLayer::getKernelSize, "kernel_size_nd"), utils::deprecateMember(&IConvolutionLayer::setKernelSize, "kernel_size_nd")) .def_property("num_output_maps", &IConvolutionLayer::getNbOutputMaps, &IConvolutionLayer::setNbOutputMaps) - .def_property("stride", &IConvolutionLayer::getStride, &IConvolutionLayer::setStride) - .def_property("padding", &IConvolutionLayer::getPadding, &IConvolutionLayer::setPadding) + .def_property("stride", utils::deprecateMember(&IConvolutionLayer::getStride, "stride_nd"), utils::deprecateMember(&IConvolutionLayer::setStride, "stride_nd")) + .def_property("padding", utils::deprecateMember(&IConvolutionLayer::getPadding, "padding_nd"), utils::deprecateMember(&IConvolutionLayer::setPadding, "padding_nd")) .def_property("pre_padding", &IConvolutionLayer::getPrePadding, &IConvolutionLayer::setPrePadding) .def_property("post_padding", &IConvolutionLayer::getPostPadding, &IConvolutionLayer::setPostPadding) .def_property("padding_mode", &IConvolutionLayer::getPaddingMode, &IConvolutionLayer::setPaddingMode) @@ -316,7 +315,7 @@ namespace tensorrt // Return numpy arrays instead of weights. .def_property("kernel", lambdas::conv_get_kernel, py::cpp_function(&IConvolutionLayer::setKernelWeights, py::keep_alive<1, 2>{})) .def_property("bias", lambdas::conv_get_bias, py::cpp_function(&IConvolutionLayer::setBiasWeights, py::keep_alive<1, 2>{})) - .def_property("dilation", &IConvolutionLayer::getDilation, &IConvolutionLayer::setDilation) + .def_property("dilation", utils::deprecateMember(&IConvolutionLayer::getDilation, "dilation_nd"), utils::deprecateMember(&IConvolutionLayer::setDilation, "dilation_nd")) .def_property("kernel_size_nd", &IConvolutionLayer::getKernelSizeNd, &IConvolutionLayer::setKernelSizeNd) .def_property("stride_nd", &IConvolutionLayer::getStrideNd, &IConvolutionLayer::setStrideNd) .def_property("padding_nd", &IConvolutionLayer::getPaddingNd, &IConvolutionLayer::setPaddingNd) @@ -360,9 +359,9 @@ namespace tensorrt py::class_>(m, "IPoolingLayer", IPoolingLayerDoc::descr) .def_property("type", &IPoolingLayer::getPoolingType, &IPoolingLayer::setPoolingType) - .def_property("window_size", &IPoolingLayer::getWindowSize, &IPoolingLayer::setWindowSize) - .def_property("stride", &IPoolingLayer::getStride, &IPoolingLayer::setStride) - .def_property("padding", &IPoolingLayer::getPadding, &IPoolingLayer::setPadding) + .def_property("window_size", utils::deprecateMember(&IPoolingLayer::getWindowSize, "windnow_size_nd"), utils::deprecateMember(&IPoolingLayer::setWindowSize, "windnow_size_nd")) + .def_property("stride", utils::deprecateMember(&IPoolingLayer::getStride, "stride_nd"), utils::deprecateMember(&IPoolingLayer::setStride, "stride_nd")) + .def_property("padding", utils::deprecateMember(&IPoolingLayer::getPadding, "padding_nd"), utils::deprecateMember(&IPoolingLayer::setPadding, "padding_nd")) .def_property("pre_padding", &IPoolingLayer::getPrePadding, &IPoolingLayer::setPrePadding) .def_property("post_padding", &IPoolingLayer::getPostPadding, &IPoolingLayer::setPostPadding) .def_property("padding_mode", &IPoolingLayer::getPaddingMode, &IPoolingLayer::setPaddingMode) @@ -392,7 +391,15 @@ namespace tensorrt .def_property("shift", lambdas::scale_get_shift, py::cpp_function(&IScaleLayer::setShift, py::keep_alive<1, 2>{})) .def_property("scale", lambdas::scale_get_scale, py::cpp_function(&IScaleLayer::setScale, py::keep_alive<1, 2>{})) .def_property("power", lambdas::scale_get_power, py::cpp_function(&IScaleLayer::setPower, py::keep_alive<1, 2>{})) - .def_property_readonly("channel_axis", &IScaleLayer::getChannelAxis) + .def_property("channel_axis", &IScaleLayer::getChannelAxis, &IScaleLayer::setChannelAxis) + ; + + py::class_>(m, "IQuantizeLayer", IQuantizeLayerDoc::descr) + .def_property("axis", &IQuantizeLayer::getAxis, &IQuantizeLayer::setAxis) + ; + + py::class_>(m, "IDequantizeLayer", IDequantizeLayerDoc::descr) + .def_property("axis", &IDequantizeLayer::getAxis, &IDequantizeLayer::setAxis) ; py::class_>(m, "ISoftMaxLayer", ISoftMaxLayerDoc::descr) @@ -404,10 +411,10 @@ namespace tensorrt ; py::class_>(m, "IDeconvolutionLayer", IDeconvolutionLayerDoc::descr) - .def_property("kernel_size", &IDeconvolutionLayer::getKernelSize, &IDeconvolutionLayer::setKernelSize) + .def_property("kernel_size", utils::deprecateMember(&IDeconvolutionLayer::getKernelSize, "kernel_size_nd"), utils::deprecateMember(&IDeconvolutionLayer::setKernelSize, "kernel_size_nd")) + .def_property("stride", utils::deprecateMember(&IDeconvolutionLayer::getStride, "stride_nd"), utils::deprecateMember(&IDeconvolutionLayer::setStride, "stride_nd")) + .def_property("padding", utils::deprecateMember(&IDeconvolutionLayer::getPadding, "padding_nd"), utils::deprecateMember(&IDeconvolutionLayer::setPadding, "padding_nd")) .def_property("num_output_maps", &IDeconvolutionLayer::getNbOutputMaps, &IDeconvolutionLayer::setNbOutputMaps) - .def_property("stride", &IDeconvolutionLayer::getStride, &IDeconvolutionLayer::setStride) - .def_property("padding", &IDeconvolutionLayer::getPadding, &IDeconvolutionLayer::setPadding) .def_property("pre_padding", &IDeconvolutionLayer::getPrePadding, &IDeconvolutionLayer::setPrePadding) .def_property("post_padding", &IDeconvolutionLayer::getPostPadding, &IDeconvolutionLayer::setPostPadding) .def_property("padding_mode", &IDeconvolutionLayer::getPaddingMode, &IDeconvolutionLayer::setPaddingMode) @@ -464,20 +471,6 @@ namespace tensorrt .value("SKIP", RNNInputMode::kSKIP, RNNInputModeDoc::SKIP) ; - py::class_>(m, "IRNNLayer", IRNNLayerDoc::descr) - .def_property_readonly("num_layers", &IRNNLayer::getLayerCount) - .def_property_readonly("hidden_size", &IRNNLayer::getHiddenSize) - .def_property_readonly("max_seq_length", &IRNNLayer::getSeqLength) - .def_property("op", &IRNNLayer::getOperation, &IRNNLayer::setOperation) - .def_property("input_mode", &IRNNLayer::getInputMode, &IRNNLayer::setInputMode) - .def_property("direction", &IRNNLayer::getDirection, &IRNNLayer::setDirection) - .def_property("weights", lambdas::rnn_get_weights, py::cpp_function(&IRNNLayer::setWeights, py::keep_alive<1, 2>{})) - .def_property("bias", lambdas::rnn_get_bias, py::cpp_function(&IRNNLayer::setBias, py::keep_alive<1, 2>{})) - .def_property_readonly("data_length", &IRNNLayer::getDataLength) - .def_property("hidden_state", &IRNNLayer::getHiddenState, &IRNNLayer::setHiddenState) - .def_property("cell_state", &IRNNLayer::getCellState, &IRNNLayer::setCellState) - ; - py::enum_(m, "RNNGateType", RNNGateTypeDoc::descr) .value("INPUT", RNNGateType::kINPUT, RNNGateTypeDoc::INPUT) .value("OUTPUT", RNNGateType::kOUTPUT, RNNGateTypeDoc::OUTPUT) @@ -505,14 +498,6 @@ namespace tensorrt .def_property("cell_state", &IRNNv2Layer::getCellState, py::cpp_function(&IRNNv2Layer::setCellState, py::keep_alive<1, 2>{})) ; - py::class_>(m, "IOutputDimensionsFormula", IOutputDimensionsFormulaDoc::descr) - .def("compute", &IOutputDimensionsFormula::compute, "input_shape"_a, "kernel_shape"_a, "stride"_a, "padding"_a, "dilation"_a, "layer_name"_a, IOutputDimensionsFormulaDoc::compute) - ; - - py::class_>(m, "IPluginLayer", IPluginLayerDoc::descr) - .def_property_readonly("plugin", &IPluginLayer::getPlugin) - ; - py::class_>(m, "IPluginV2Layer", IPluginV2LayerDoc::descr) .def_property_readonly("plugin", &IPluginV2Layer::getPlugin) ; @@ -560,8 +545,8 @@ namespace tensorrt ; py::class_>(m, "IPaddingLayer", IPaddingLayerDoc::descr) - .def_property("pre_padding", &IPaddingLayer::getPrePadding, &IPaddingLayer::setPrePadding) - .def_property("post_padding", &IPaddingLayer::getPostPadding, &IPaddingLayer::setPostPadding) + .def_property("pre_padding", utils::deprecateMember(&IPaddingLayer::getPrePadding, "pre_padding_nd"), utils::deprecateMember(&IPaddingLayer::setPrePadding, "pre_padding_nd")) + .def_property("post_padding", utils::deprecateMember(&IPaddingLayer::getPostPadding, "post_padding_nd"), utils::deprecateMember(&IPaddingLayer::setPostPadding, "post_padding_nd")) .def_property("pre_padding_nd", &IPaddingLayer::getPrePaddingNd, &IPaddingLayer::setPrePaddingNd) .def_property("post_padding_nd", &IPaddingLayer::getPostPaddingNd, &IPaddingLayer::setPostPaddingNd) ; @@ -624,8 +609,6 @@ namespace tensorrt py::class_>(m, "IMatrixMultiplyLayer", IMatrixMultiplyLayerDoc::descr) .def_property("op0", [](IMatrixMultiplyLayer& self) {return self.getOperation(0);}, [](IMatrixMultiplyLayer& self, MatrixOperation op) {return self.setOperation(0, op);}) .def_property("op1", [](IMatrixMultiplyLayer& self) {return self.getOperation(1);}, [](IMatrixMultiplyLayer& self, MatrixOperation op) {return self.setOperation(1, op);}) - .def_property("transpose0", [](IMatrixMultiplyLayer& self) {return self.getTranspose(0);}, [](IMatrixMultiplyLayer& self, bool transpose) {return self.setTranspose(0, transpose);}) - .def_property("transpose1", [](IMatrixMultiplyLayer& self) {return self.getTranspose(1);}, [](IMatrixMultiplyLayer& self, bool transpose) {return self.setTranspose(1, transpose);}) ; py::class_>(m, "IRaggedSoftMaxLayer", IRaggedSoftMaxLayerDoc::descr); @@ -645,11 +628,31 @@ namespace tensorrt .value("LINEAR", ResizeMode::kLINEAR, ResizeModeDoc::LINEAR) ; // ResizeMode + py::enum_(m, "ResizeCoordinateTransformation", ResizeCoordinateTransformationDoc::descr) + .value("ALIGN_CORNERS", ResizeCoordinateTransformation::kALIGN_CORNERS, ResizeCoordinateTransformationDoc::ALIGN_CORNERS) + .value("ASYMMETRIC", ResizeCoordinateTransformation::kASYMMETRIC, ResizeCoordinateTransformationDoc::ASYMMETRIC) + .value("HALF_PIXEL", ResizeCoordinateTransformation::kHALF_PIXEL, ResizeCoordinateTransformationDoc::HALF_PIXEL) + ; // ResizeCoordinateTransformation + + py::enum_(m, "ResizeSelector", ResizeSelectorDoc::descr) + .value("FORMULA", ResizeSelector::kFORMULA,ResizeSelectorDoc::FORMULA) + .value("UPPER", ResizeSelector::kUPPER, ResizeSelectorDoc::UPPER) + ; // ResizeSelector + + py::enum_(m, "ResizeRoundMode", ResizeRoundModeDoc::descr) + .value("HALF_UP", ResizeRoundMode::kHALF_UP,ResizeRoundModeDoc::HALF_UP) + .value("HALF_DOWN", ResizeRoundMode::kHALF_DOWN, ResizeRoundModeDoc::HALF_DOWN) + .value("FLOOR", ResizeRoundMode::kFLOOR,ResizeRoundModeDoc::FLOOR) + .value("CEIL", ResizeRoundMode::kCEIL, ResizeRoundModeDoc::CEIL) + ; // ResizeRoundMode + py::class_>(m, "IResizeLayer", IResizeLayerDoc::descr) .def_property("shape", &IResizeLayer::getOutputDimensions, &IResizeLayer::setOutputDimensions) .def_property("scales", lambdas::resize_get_scales, lambdas::resize_set_scales) .def_property("resize_mode", &IResizeLayer::getResizeMode, &IResizeLayer::setResizeMode) - .def_property("align_corners", &IResizeLayer::getAlignCorners, &IResizeLayer::setAlignCorners) + .def_property("coordinate_transformation", &IResizeLayer::getCoordinateTransformation, &IResizeLayer::setCoordinateTransformation) + .def_property("selector_for_single_pixel", &IResizeLayer::getSelectorForSinglePixel, &IResizeLayer::setSelectorForSinglePixel) + .def_property("nearest_rounding", &IResizeLayer::getNearestRounding, &IResizeLayer::setNearestRounding ) .def("set_input", &IResizeLayer::setInput, "index"_a, "tensor"_a, IResizeLayerDoc::set_input) ; @@ -684,7 +687,7 @@ namespace tensorrt py::class_>(m, "IIteratorLayer", IIteratorLayerDoc::descr) .def_property("axis", &IIteratorLayer::getAxis, &IIteratorLayer::setAxis) - .def_property("reverse", &IIteratorLayer::getReverse, &IIteratorLayer::getReverse) + .def_property("reverse", &IIteratorLayer::getReverse, &IIteratorLayer::setReverse) ; py::class_>(m, "ILoop", ILoopDoc::descr) @@ -713,22 +716,21 @@ namespace tensorrt // Weights must be kept alive for the duration of the network. py::keep_alive is critical here! // Additionally, we use reference_internal so that pybind11 does not free layers when they go out of scope. - py::class_ >(m, "INetworkDefinition", INetworkDefinitionDoc::descr) + py::class_(m, "INetworkDefinition", INetworkDefinitionDoc::descr) .def_property("name", &INetworkDefinition::getName, &INetworkDefinition::setName) - .def_property("pooling_output_dimensions_formula", &INetworkDefinition::getPoolingOutputDimensionsFormula, &INetworkDefinition::setPoolingOutputDimensionsFormula) - .def_property("convolution_output_dimensions_formula", &INetworkDefinition::getConvolutionOutputDimensionsFormula, &INetworkDefinition::setConvolutionOutputDimensionsFormula) - .def_property("deconvolution_output_dimensions_formula", &INetworkDefinition::getDeconvolutionOutputDimensionsFormula, &INetworkDefinition::setDeconvolutionOutputDimensionsFormula) .def_property_readonly("num_layers", &INetworkDefinition::getNbLayers) .def_property_readonly("num_inputs", &INetworkDefinition::getNbInputs) .def_property_readonly("num_outputs", &INetworkDefinition::getNbOutputs) .def_property_readonly("has_implicit_batch_dimension", &INetworkDefinition::hasImplicitBatchDimension) .def_property_readonly("has_explicit_precision", &INetworkDefinition::hasExplicitPrecision) + .def_property("error_recorder", &INetworkDefinition::getErrorRecorder, + py::cpp_function(&INetworkDefinition::setErrorRecorder, py::keep_alive<1, 2>{})) .def("mark_output", &INetworkDefinition::markOutput, "tensor"_a, INetworkDefinitionDoc::mark_output) // Layers .def("add_input", &INetworkDefinition::addInput, "name"_a, "dtype"_a, "shape"_a, INetworkDefinitionDoc::add_input, py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) - .def("add_convolution", lambdas::add_convolution, "input"_a, "num_output_maps"_a, "kernel_shape"_a, + .def("add_convolution", utils::deprecate(lambdas::add_convolution, "add_convolution_nd"), "input"_a, "num_output_maps"_a, "kernel_shape"_a, "kernel"_a, "bias"_a=nullptr, py::keep_alive<1, 5>{}, py::keep_alive<1, 6>{}, INetworkDefinitionDoc::add_convolution, py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) .def("add_convolution_nd", lambdas::add_convolution_nd, "input"_a, "num_output_maps"_a, @@ -741,7 +743,7 @@ namespace tensorrt .def("add_activation", &INetworkDefinition::addActivation, "input"_a, "type"_a, INetworkDefinitionDoc::add_activation, py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) - .def("add_pooling", &INetworkDefinition::addPooling, "input"_a, "type"_a, "window_size"_a, + .def("add_pooling", utils::deprecateMember(&INetworkDefinition::addPooling, "add_pooling_nd"), "input"_a, "type"_a, "window_size"_a, INetworkDefinitionDoc::add_pooling, py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) .def("add_pooling_nd", &INetworkDefinition::addPoolingNd, "input"_a, "type"_a, "window_size"_a, @@ -760,7 +762,7 @@ namespace tensorrt py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) .def("add_concatenation", lambdas::add_concatenation, "inputs"_a, INetworkDefinitionDoc::add_concatenation, py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) - .def("add_deconvolution", lambdas::add_deconvolution, "input"_a, "num_output_maps"_a, + .def("add_deconvolution", utils::deprecate(lambdas::add_deconvolution, "add_deconvolution_nd"), "input"_a, "num_output_maps"_a, "kernel_shape"_a, "kernel"_a, "bias"_a=nullptr, py::keep_alive<1, 5>{}, py::keep_alive<1, 6>{}, INetworkDefinitionDoc::add_deconvolution, py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) @@ -771,15 +773,9 @@ namespace tensorrt .def("add_elementwise", &INetworkDefinition::addElementWise, "input1"_a, "input2"_a, "op"_a, INetworkDefinitionDoc::add_elementwise, py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) - .def("add_rnn", &INetworkDefinition::addRNN, "input"_a, "layer_count"_a, "hidden_size"_a, - "max_seq_length"_a, "op"_a, "mode"_a, "direction"_a, "weights"_a, "bias"_a, py::keep_alive<1, 9>{}, - py::keep_alive<1, 10>{}, INetworkDefinitionDoc::add_rnn, - py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) - .def("add_plugin", lambdas::add_plugin, "inputs"_a, "plugin"_a, INetworkDefinitionDoc::add_plugin, - py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) .def("add_unary", &INetworkDefinition::addUnary, "input"_a, "op"_a, INetworkDefinitionDoc::add_unary, py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) - .def("add_padding", &INetworkDefinition::addPadding, "input"_a, "pre_padding"_a, "post_padding"_a, + .def("add_padding", utils::deprecateMember(&INetworkDefinition::addPadding, "add_padding_nd"), "input"_a, "pre_padding"_a, "post_padding"_a, INetworkDefinitionDoc::add_padding, py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) .def("add_padding_nd", &INetworkDefinition::addPaddingNd, "input"_a, "pre_padding"_a, "post_padding"_a, @@ -806,19 +802,12 @@ namespace tensorrt static_cast(&INetworkDefinition::addMatrixMultiply), "input0"_a, "op0"_a, "input1"_a, "op1"_a, INetworkDefinitionDoc::add_matrix_multiply, py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) - .def("add_matrix_multiply_deprecated", - static_cast(&INetworkDefinition::addMatrixMultiply), "input0"_a, "transpose0"_a, "input1"_a, - "transpose1"_a, INetworkDefinitionDoc::add_matrix_multiply_deprecated, - py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) .def("add_constant", &INetworkDefinition::addConstant, "shape"_a, "weights"_a, py::keep_alive<1, 3>{}, INetworkDefinitionDoc::add_constant, py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) - .def("add_rnn_v2", &INetworkDefinition::addRNNv2, "input"_a, "layer_count"_a, + .def("add_rnn_v2", utils::deprecateMember(&INetworkDefinition::addRNNv2, "addLoop"), "input"_a, "layer_count"_a, "hidden_size"_a, "max_seq_length"_a, "op"_a, py::keep_alive<1, 0>{}, INetworkDefinitionDoc::add_rnn_v2) - .def("add_plugin_ext", lambdas::add_plugin_ext, "inputs"_a, "plugin"_a, - INetworkDefinitionDoc::add_plugin_ext, - py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) .def("add_identity", &INetworkDefinition::addIdentity, "input"_a, INetworkDefinitionDoc::add_identity, py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) @@ -838,10 +827,17 @@ namespace tensorrt "else_input"_a, INetworkDefinitionDoc::add_select, py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) .def("add_fill", &INetworkDefinition::addFill, "shape"_a, "op"_a, INetworkDefinitionDoc::add_fill) + .def("add_quantize", &INetworkDefinition::addQuantize, "input"_a, "scale"_a, + INetworkDefinitionDoc::add_quantize, + py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) + .def("add_dequantize", &INetworkDefinition::addDequantize, "input"_a, "scale"_a, + INetworkDefinitionDoc::add_dequantize, + py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) .def("remove_tensor", &INetworkDefinition::removeTensor, "tensor"_a, INetworkDefinitionDoc::remove_tensor) .def("unmark_output", &INetworkDefinition::unmarkOutput, "tensor"_a, INetworkDefinitionDoc::unmark_output) .def("mark_output_for_shapes", &INetworkDefinition::markOutputForShapes, "tensor"_a, INetworkDefinitionDoc::mark_output_for_shapes) .def("unmark_output_for_shapes", &INetworkDefinition::unmarkOutputForShapes, "tensor"_a, INetworkDefinitionDoc::unmark_output_for_shapes) + .def("set_weights_name", &INetworkDefinition::setWeightsName, "weights"_a, "name"_a, INetworkDefinitionDoc::set_weights_name) // Getters .def("get_layer", &INetworkDefinition::getLayer, "index"_a, INetworkDefinitionDoc::get_layer, py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) @@ -853,7 +849,7 @@ namespace tensorrt .def("__len__", &INetworkDefinition::getNbLayers) .def("__getitem__", lambdas::network_getitem, py::return_value_policy::reference_internal, py::keep_alive<1, 0>{}, py::return_value_policy::reference_internal) - .def("__del__", &INetworkDefinition::destroy) + .def("__del__", &utils::doNothingDel) ; } diff --git a/python/src/infer/pyInt8.cpp b/python/src/infer/pyInt8.cpp index 82047f5a..f00d7d6e 100644 --- a/python/src/infer/pyInt8.cpp +++ b/python/src/infer/pyInt8.cpp @@ -15,191 +15,211 @@ */ // This contains int8 calibration related things. -#include "NvInfer.h" -#include "utils.h" -#include "infer/pyInt8Doc.h" #include "ForwardDeclarations.h" -// For vector support +#include "infer/pyInt8Doc.h" +#include "utils.h" #include using namespace nvinfer1; namespace tensorrt { - // Use CRTP to share code among several different classes. - template - class pyCalibratorTrampoline : public Derived +// Use CRTP to share code among several different classes. +template +class pyCalibratorTrampoline : public Derived +{ +public: + using Derived::Derived; // Inherit constructors + + int getBatchSize() const noexcept override { - public: - using Derived::Derived; // Inherit constructors + PYBIND11_OVERLOAD_PURE_NAME(int, Derived, "get_batch_size", getBatchSize); + } - int getBatchSize() const override - { - PYBIND11_OVERLOAD_PURE_NAME(int, Derived, "get_batch_size", getBatchSize); - } - - bool getBatch(void* bindings[], const char* names[], int nbBindings) override - { - py::gil_scoped_acquire gil{}; - - py::function pyGetBatch = utils::getOverload(static_cast(this), "get_batch"); - std::vector namesVec(names, names + nbBindings); - py::object result = pyGetBatch(namesVec); - // Copy over into the other data structure. - if (!result.is_none() && result.cast>().size() != 0) - { - std::memcpy(bindings, result.cast>().data(), nbBindings * sizeof(void*)); - return true; - } - return false; - } - - const void* readCalibrationCache(std::size_t& length) override - { - py::gil_scoped_acquire gil{}; - - py::function pyReadCalibrationCache = utils::getOverload(static_cast(this), "read_calibration_cache"); - py::buffer cache = pyReadCalibrationCache(); - if (!cache.is_none()) - { - py::buffer_info info = cache.request(); - length = info.size * info.itemsize; - return info.ptr; - } - return nullptr; - } - - void writeCalibrationCache(const void* ptr, std::size_t length) override - { - py::gil_scoped_acquire gil{}; - - py::function pyWriteCalibrationCache = utils::getOverload(static_cast(this), "write_calibration_cache"); - py::buffer_info info{ - const_cast(ptr), /* Pointer to buffer */ - sizeof(char), /* Size of one scalar */ - py::format_descriptor::format(), /* Python struct-style format descriptor */ - 1, /* Number of dimensions */ - { length }, /* Buffer dimensions */ - { sizeof(char) } /* Strides (in bytes) for each index */ - }; - py::memoryview cache{info}; - pyWriteCalibrationCache(cache); - } - }; - - - class pyIInt8Calibrator : public pyCalibratorTrampoline + bool getBatch(void* bindings[], const char* names[], int nbBindings) noexcept override { - public: - using Derived = pyCalibratorTrampoline; - using Derived::Derived; + py::gil_scoped_acquire gil{}; - CalibrationAlgoType getAlgorithm() override + py::function pyGetBatch = utils::getOverload(static_cast(this), "get_batch"); + std::vector namesVec(names, names + nbBindings); + py::object result = pyGetBatch(namesVec); + // Copy over into the other data structure. + if (!result.is_none() && result.cast>().size() != 0) { - PYBIND11_OVERLOAD_PURE_NAME(CalibrationAlgoType, IInt8Calibrator, "get_algorithm", getAlgorithm); + std::memcpy(bindings, result.cast>().data(), nbBindings * sizeof(void*)); + return true; } - }; + return false; + } - class pyIInt8LegacyCalibrator : public pyCalibratorTrampoline + const void* readCalibrationCache(std::size_t& length) noexcept override { - public: - using Derived = pyCalibratorTrampoline; - using Derived::Derived; + py::gil_scoped_acquire gil{}; - double getQuantile() const override + py::function pyReadCalibrationCache = utils::getOverload(static_cast(this), "read_calibration_cache"); + py::buffer cache = pyReadCalibrationCache(); + if (!cache.is_none()) { - PYBIND11_OVERLOAD_PURE_NAME(double, IInt8LegacyCalibrator, "get_quantile", getQuantile); + py::buffer_info info = cache.request(); + length = info.size * info.itemsize; + return info.ptr; } + return nullptr; + } - double getRegressionCutoff() const override - { - PYBIND11_OVERLOAD_PURE_NAME(double, IInt8LegacyCalibrator, "get_regression_cutoff", getRegressionCutoff); - } - - const void* readHistogramCache(std::size_t& length) override - { - PYBIND11_OVERLOAD_PURE_NAME(const void*, IInt8LegacyCalibrator, "read_histogram_cache", readHistogramCache, length); - } - - void writeHistogramCache(const void* ptr, std::size_t length) override - { - PYBIND11_OVERLOAD_PURE_NAME(void, IInt8LegacyCalibrator, "write_histogram_cache", writeHistogramCache, ptr, length); - } - }; - - - template - std::vector docGetBatch(T&, const std::vector&) {} - - template - py::buffer docReadCalibrationCache(T&) {} - - template - void docWriteCalibrationCache(T&, py::buffer) {} - - - void bindInt8(py::module& m) + void writeCalibrationCache(const void* ptr, std::size_t length) noexcept override { - py::enum_(m, "CalibrationAlgoType", CalibrationAlgoTypeDoc::descr) - .value("LEGACY_CALIBRATION", CalibrationAlgoType::kLEGACY_CALIBRATION) - .value("ENTROPY_CALIBRATION", CalibrationAlgoType::kENTROPY_CALIBRATION) - .value("ENTROPY_CALIBRATION_2", CalibrationAlgoType::kENTROPY_CALIBRATION_2) - .value("MINMAX_CALIBRATION", CalibrationAlgoType::kMINMAX_CALIBRATION) - ; + py::gil_scoped_acquire gil{}; - // NOTE: Fake bindings are provided for some of the application-implemented functions here. - // These are solely for documentation purposes. The user is meant to override these functions - // in their own code, and the bindings here will never be called. + py::function pyWriteCalibrationCache + = utils::getOverload(static_cast(this), "write_calibration_cache"); - py::class_(m, "IInt8Calibrator", IInt8CalibratorDoc::descr) - .def(py::init_alias<>()) // Always initialize trampoline class. - .def("get_batch_size", &IInt8Calibrator::getBatchSize, IInt8CalibratorDoc::get_batch_size) - .def("get_algorithm", &IInt8Calibrator::getAlgorithm, IInt8CalibratorDoc::get_algorithm) - // For documentation purposes only - .def("get_batch", docGetBatch, "names"_a, IInt8CalibratorDoc::get_batch) - .def("read_calibration_cache", docReadCalibrationCache, IInt8CalibratorDoc::read_calibration_cache) - .def("write_calibration_cache", docWriteCalibrationCache, "cache"_a, IInt8CalibratorDoc::write_calibration_cache) - ; +#if PYBIND11_VERSION_MAJOR < 2 || PYBIND11_VERSION_MAJOR == 2 && PYBIND11_VERSION_MINOR < 6 + py::buffer_info info{ + const_cast(ptr), /* Pointer to buffer */ + sizeof(uint8_t), /* Size of one scalar */ + py::format_descriptor::format(), /* Python struct-style format descriptor */ + 1, /* Number of dimensions */ + {length}, /* Buffer dimensions */ + { sizeof(uint8_t) } /* Strides (in bytes) for each index */ + }; + py::memoryview cache{info}; +#else + py::memoryview cache{ + py::memoryview::from_buffer(static_cast(ptr), {length}, {sizeof(uint8_t)})}; +#endif + pyWriteCalibrationCache(cache); + } +}; - py::class_(m, "IInt8LegacyCalibrator", IInt8LegacyCalibratorDoc::descr) - .def(py::init_alias<>()) // Always initialize trampoline class. - .def("get_batch_size", &IInt8LegacyCalibrator::getBatchSize, IInt8CalibratorDoc::get_batch_size) - .def("get_algorithm", &IInt8LegacyCalibrator::getAlgorithm, IInt8LegacyCalibratorDoc::get_algorithm) - // For documentation purposes only - .def("get_batch", docGetBatch, "names"_a, IInt8CalibratorDoc::get_batch) - .def("read_calibration_cache", docReadCalibrationCache, IInt8CalibratorDoc::read_calibration_cache) - .def("write_calibration_cache", docWriteCalibrationCache, "cache"_a, IInt8CalibratorDoc::write_calibration_cache) - ; +class pyIInt8Calibrator : public pyCalibratorTrampoline +{ +public: + using Derived = pyCalibratorTrampoline; + using Derived::Derived; - py::class_>(m, "IInt8EntropyCalibrator", IInt8EntropyCalibratorDoc::descr) - .def(py::init_alias<>()) // Always initialize trampoline class. - .def("get_batch_size", &IInt8EntropyCalibrator::getBatchSize, IInt8CalibratorDoc::get_batch_size) - .def("get_algorithm", &IInt8EntropyCalibrator::getAlgorithm, IInt8EntropyCalibratorDoc::get_algorithm) - // For documentation purposes only - .def("get_batch", docGetBatch, "names"_a, IInt8CalibratorDoc::get_batch) - .def("read_calibration_cache", docReadCalibrationCache, IInt8CalibratorDoc::read_calibration_cache) - .def("write_calibration_cache", docWriteCalibrationCache, "cache"_a, IInt8CalibratorDoc::write_calibration_cache) - ; + CalibrationAlgoType getAlgorithm() noexcept override + { + PYBIND11_OVERLOAD_PURE_NAME(CalibrationAlgoType, IInt8Calibrator, "get_algorithm", getAlgorithm); + } +}; - py::class_>(m, "IInt8EntropyCalibrator2", IInt8EntropyCalibrator2Doc::descr) - .def(py::init_alias<>()) // Always initialize trampoline class. - .def("get_batch_size", &IInt8EntropyCalibrator2::getBatchSize, IInt8CalibratorDoc::get_batch_size) - .def("get_algorithm", &IInt8EntropyCalibrator2::getAlgorithm, IInt8EntropyCalibrator2Doc::get_algorithm) - // For documentation purposes only - .def("get_batch", docGetBatch, "names"_a, IInt8CalibratorDoc::get_batch) - .def("read_calibration_cache", docReadCalibrationCache, IInt8CalibratorDoc::read_calibration_cache) - .def("write_calibration_cache", docWriteCalibrationCache, "cache"_a, IInt8CalibratorDoc::write_calibration_cache) - ; +class pyIInt8LegacyCalibrator : public pyCalibratorTrampoline +{ +public: + using Derived = pyCalibratorTrampoline; + using Derived::Derived; - py::class_>(m, "IInt8MinMaxCalibrator", IInt8MinMaxCalibratorDoc::descr) - .def(py::init_alias<>()) // Always initialize trampoline class. - .def("get_batch_size", &IInt8MinMaxCalibrator::getBatchSize, IInt8CalibratorDoc::get_batch_size) - .def("get_algorithm", &IInt8MinMaxCalibrator::getAlgorithm, IInt8MinMaxCalibratorDoc::get_algorithm) - // For documentation purposes only - .def("get_batch", docGetBatch, "names"_a, IInt8CalibratorDoc::get_batch) - .def("read_calibration_cache", docReadCalibrationCache, IInt8CalibratorDoc::read_calibration_cache) - .def("write_calibration_cache", docWriteCalibrationCache, "cache"_a, IInt8CalibratorDoc::write_calibration_cache) - ; + double getQuantile() const noexcept override + { + PYBIND11_OVERLOAD_PURE_NAME(double, IInt8LegacyCalibrator, "get_quantile", getQuantile); + } - } // Int8 -} /* tensorrt */ + double getRegressionCutoff() const noexcept override + { + PYBIND11_OVERLOAD_PURE_NAME(double, IInt8LegacyCalibrator, "get_regression_cutoff", getRegressionCutoff); + } + + const void* readHistogramCache(std::size_t& length) noexcept override + { + PYBIND11_OVERLOAD_PURE_NAME( + const void*, IInt8LegacyCalibrator, "read_histogram_cache", readHistogramCache, length); + } + + void writeHistogramCache(const void* ptr, std::size_t length) noexcept override + { + PYBIND11_OVERLOAD_PURE_NAME( + void, IInt8LegacyCalibrator, "write_histogram_cache", writeHistogramCache, ptr, length); + } +}; + +// NOTE: Fake bindings are provided for some of the application-implemented functions here. +// These are solely for documentation purposes. The user is meant to override these functions +// in their own code, and the bindings here will never be called. + +template +std::vector docGetBatch(T&, const std::vector&) +{ + return {}; +} + +template +py::buffer docReadCalibrationCache(T&) +{ + return {}; +} + +template +void docWriteCalibrationCache(T&, py::buffer) +{ +} + +void bindInt8(py::module& m) +{ + py::enum_(m, "CalibrationAlgoType", CalibrationAlgoTypeDoc::descr) + .value("LEGACY_CALIBRATION", CalibrationAlgoType::kLEGACY_CALIBRATION) + .value("ENTROPY_CALIBRATION", CalibrationAlgoType::kENTROPY_CALIBRATION) + .value("ENTROPY_CALIBRATION_2", CalibrationAlgoType::kENTROPY_CALIBRATION_2) + .value("MINMAX_CALIBRATION", CalibrationAlgoType::kMINMAX_CALIBRATION); + + py::class_(m, "IInt8Calibrator", IInt8CalibratorDoc::descr) + .def(py::init<>()) + .def("get_batch_size", &IInt8Calibrator::getBatchSize, IInt8CalibratorDoc::get_batch_size) + .def("get_algorithm", &IInt8Calibrator::getAlgorithm, IInt8CalibratorDoc::get_algorithm) + // For documentation purposes only + .def("get_batch", docGetBatch, "names"_a, IInt8CalibratorDoc::get_batch) + .def("read_calibration_cache", docReadCalibrationCache, + IInt8CalibratorDoc::read_calibration_cache) + .def("write_calibration_cache", docWriteCalibrationCache, "cache"_a, + IInt8CalibratorDoc::write_calibration_cache); + + py::class_( + m, "IInt8LegacyCalibrator", IInt8LegacyCalibratorDoc::descr) + .def(py::init<>()) + .def("get_batch_size", &IInt8LegacyCalibrator::getBatchSize, IInt8CalibratorDoc::get_batch_size) + .def("get_algorithm", &IInt8LegacyCalibrator::getAlgorithm, IInt8LegacyCalibratorDoc::get_algorithm) + // For documentation purposes only + .def("get_batch", docGetBatch, "names"_a, IInt8CalibratorDoc::get_batch) + .def("read_calibration_cache", docReadCalibrationCache, + IInt8CalibratorDoc::read_calibration_cache) + .def("write_calibration_cache", docWriteCalibrationCache, "cache"_a, + IInt8CalibratorDoc::write_calibration_cache); + + py::class_>( + m, "IInt8EntropyCalibrator", IInt8EntropyCalibratorDoc::descr) + .def(py::init<>()) + .def("get_batch_size", &IInt8EntropyCalibrator::getBatchSize, IInt8CalibratorDoc::get_batch_size) + .def("get_algorithm", &IInt8EntropyCalibrator::getAlgorithm, IInt8EntropyCalibratorDoc::get_algorithm) + // For documentation purposes only + .def("get_batch", docGetBatch, "names"_a, IInt8CalibratorDoc::get_batch) + .def("read_calibration_cache", docReadCalibrationCache, + IInt8CalibratorDoc::read_calibration_cache) + .def("write_calibration_cache", docWriteCalibrationCache, "cache"_a, + IInt8CalibratorDoc::write_calibration_cache); + + py::class_>( + m, "IInt8EntropyCalibrator2", IInt8EntropyCalibrator2Doc::descr) + .def(py::init<>()) + .def("get_batch_size", &IInt8EntropyCalibrator2::getBatchSize, IInt8CalibratorDoc::get_batch_size) + .def("get_algorithm", &IInt8EntropyCalibrator2::getAlgorithm, IInt8EntropyCalibrator2Doc::get_algorithm) + // For documentation purposes only + .def("get_batch", docGetBatch, "names"_a, IInt8CalibratorDoc::get_batch) + .def("read_calibration_cache", docReadCalibrationCache, + IInt8CalibratorDoc::read_calibration_cache) + .def("write_calibration_cache", docWriteCalibrationCache, "cache"_a, + IInt8CalibratorDoc::write_calibration_cache); + + py::class_>( + m, "IInt8MinMaxCalibrator", IInt8MinMaxCalibratorDoc::descr) + .def(py::init<>()) + .def("get_batch_size", &IInt8MinMaxCalibrator::getBatchSize, IInt8CalibratorDoc::get_batch_size) + .def("get_algorithm", &IInt8MinMaxCalibrator::getAlgorithm, IInt8MinMaxCalibratorDoc::get_algorithm) + // For documentation purposes only + .def("get_batch", docGetBatch, "names"_a, IInt8CalibratorDoc::get_batch) + .def("read_calibration_cache", docReadCalibrationCache, + IInt8CalibratorDoc::read_calibration_cache) + .def("write_calibration_cache", docWriteCalibrationCache, "cache"_a, + IInt8CalibratorDoc::write_calibration_cache); + +} // Int8 +} // namespace tensorrt diff --git a/python/src/infer/pyPlugin.cpp b/python/src/infer/pyPlugin.cpp index 89a615ed..e5e1770b 100644 --- a/python/src/infer/pyPlugin.cpp +++ b/python/src/infer/pyPlugin.cpp @@ -16,241 +16,243 @@ // This file contains all bindings related to plugins. #include "ForwardDeclarations.h" -#include "NvInfer.h" -#include "NvInferPlugin.h" #include "infer/pyPluginDoc.h" -// For vector support -#include #include +#include + namespace tensorrt { - using namespace nvinfer1; - using namespace nvinfer1::plugin; +using namespace nvinfer1; +using namespace nvinfer1::plugin; - // Long lambda functions should go here rather than being inlined into the bindings (1 liners are OK). - namespace lambdas +constexpr PluginFieldCollection EMPTY_PLUGIN_FIELD_COLLECTION{0, nullptr}; + +// Long lambda functions should go here rather than being inlined into the bindings (1 liners are OK). +namespace lambdas +{ +// For IPluginV2 +static const auto IPluginV2_get_output_shape = [](IPluginV2& self, int index, const std::vector inputShapes) { + return self.getOutputDimensions(index, inputShapes.data(), inputShapes.size()); +}; + +static const auto IPluginV2_configure_with_format + = [](IPluginV2& self, const std::vector inputShapes, const std::vector outputShapes, DataType dtype, + TensorFormat format, int maxBatchSize) { + return self.configureWithFormat(inputShapes.data(), inputShapes.size(), outputShapes.data(), + outputShapes.size(), dtype, format, maxBatchSize); + }; + +static const auto IPluginV2_serialize = [](IPluginV2& self) { + size_t size = self.getSerializationSize(); + // Python will own and free the memory returned by this function + uint8_t* buffer = new uint8_t[size]; + self.serialize(buffer); + +#if PYBIND11_VERSION_MAJOR < 2 || PYBIND11_VERSION_MAJOR == 2 && PYBIND11_VERSION_MINOR < 6 + py::buffer_info info{ + buffer, /* Pointer to buffer */ + sizeof(uint8_t), /* Size of one scalar */ + py::format_descriptor::format(), /* Python struct-style format descriptor */ + 1, /* Number of dimensions */ + {size}, /* Buffer dimensions */ + {sizeof(uint8_t)} /* Strides (in bytes) for each index */ + }; + py::memoryview pyBuffer{info}; +#else + py::memoryview pyBuffer{py::memoryview::from_buffer(buffer, {size}, {sizeof(uint8_t)})}; +#endif + return pyBuffer; +}; + +// `const vector::data()` corresponds to `const void* const*` (pointer to const-pointer to const void) +static const auto IPluginV2_execute_async = [](IPluginV2& self, int batchSize, const std::vector& inputs, + std::vector& outputs, void* workspace, long stream) { + return self.enqueue(batchSize, inputs.data(), outputs.data(), workspace, reinterpret_cast(stream)); +}; + +// For IPluginV2Ext +static const auto get_output_data_type = [](IPluginV2Ext& self, int index, const std::vector inputTypes) { + return self.getOutputDataType(index, inputTypes.data(), inputTypes.size()); +}; + +// For IPluginV2Ext - makes copy of a vector as a bool[]. +static std::unique_ptr makeBoolArray(const std::vector& v) +{ + int n = v.size(); + std::unique_ptr result(n > 0 ? new bool[n] : nullptr); + std::copy_n(v.begin(), n, result.get()); + return std::move(result); +} + +static const auto configure_plugin + = [](IPluginV2Ext& self, const std::vector inputShapes, const std::vector outputShapes, + const std::vector inputTypes, const std::vector outputTypes, + const std::vector inputIsBroadcasted, const std::vector outputIsBroadcasted, TensorFormat format, + int maxBatchSize) { + auto inputBroadcast = makeBoolArray(inputIsBroadcasted); + auto outputBroadcast = makeBoolArray(outputIsBroadcasted); + return self.configurePlugin(inputShapes.data(), inputShapes.size(), outputShapes.data(), outputShapes.size(), + inputTypes.data(), outputTypes.data(), inputBroadcast.get(), outputBroadcast.get(), format, maxBatchSize); + }; + +static const auto attach_to_context = [](IPluginV2Ext& self, void* cudnn, void* cublas, void* allocator) { + self.attachToContext( + static_cast(cudnn), static_cast(cublas), static_cast(allocator)); +}; + +// For PluginField +static const auto plugin_field_default_constructor + = [](const FallbackString& name) { return new PluginField{name.c_str()}; }; + +static const auto plugin_field_constructor + = [](const FallbackString& name, py::buffer& data, nvinfer1::PluginFieldType type) { + py::buffer_info info = data.request(); + // PluginField length is number of entries. type gives information about the size of each entry. + return new PluginField{name.c_str(), info.ptr, type, static_cast(info.size)}; + }; + +// For PluginFieldCollection +static const auto plugin_field_collection_constructor = [](const std::vector& fields) { + return new PluginFieldCollection{static_cast(fields.size()), fields.data()}; +}; + +// For IPluginRegistry. We do an allocation here, but python takes ownership. +static const auto get_plugin_creator_list = [](IPluginRegistry& self) { + int numCreators{0}; + IPluginCreator* const* ptr = self.getPluginCreatorList(&numCreators); + // This is NOT a memory leak - python will free when done. + return new std::vector(ptr, ptr + numCreators); +}; + +// For IPluginCreator +static const auto creator_create_plugin + = [](IPluginCreator& self, const std::string& name, const PluginFieldCollection* fc) { + return self.createPlugin(name.c_str(), fc); + }; + +static const auto get_field_names = [](IPluginCreator& self) -> const PluginFieldCollection* { + const PluginFieldCollection* fieldCollection = self.getFieldNames(); + if (!fieldCollection) { - // For IPlugin - static const auto get_output_shape = [] (IPlugin& self, int index, const std::vector inputShapes) { - return self.getOutputDimensions(index, inputShapes.data(), inputShapes.size()); - }; + return &EMPTY_PLUGIN_FIELD_COLLECTION; + } + return fieldCollection; +}; - static const auto configure = [] (IPlugin& self, const std::vector inputShapes, const std::vector outputShapes, int maxBatchSize) { - return self.configure(inputShapes.data(), inputShapes.size(), outputShapes.data(), outputShapes.size(), maxBatchSize); - }; +static const auto deserialize_plugin = [](IPluginCreator& self, const std::string& name, py::buffer& serializedPlugin) { + py::buffer_info info = serializedPlugin.request(); + return self.deserializePlugin(name.c_str(), info.ptr, info.size * info.itemsize); +}; - // `const vector::data()` corresponds to `const void* const*` (pointer to const-pointer to const void) - static const auto execute_async = [] (IPlugin& self, int batchSize, const std::vector& inputs, std::vector& outputs, void* workspace, long stream) { - return self.enqueue(batchSize, inputs.data(), outputs.data(), workspace, reinterpret_cast(stream)); - }; +} // namespace lambdas - // For IPluginExt - static const auto configure_with_format = [] (IPluginExt& self, const std::vector inputShapes, const std::vector outputShapes, DataType dtype, TensorFormat format, int maxBatchSize) { - return self.configureWithFormat(inputShapes.data(), inputShapes.size(), outputShapes.data(), outputShapes.size(), dtype, format, maxBatchSize); - }; +void bindPlugin(py::module& m) +{ + py::class_(m, "IPluginV2", IPluginV2Doc::descr) + .def_property_readonly("num_outputs", &IPluginV2::getNbOutputs) + .def_property_readonly("tensorrt_version", &IPluginV2::getTensorRTVersion) + .def_property_readonly("plugin_type", &IPluginV2::getPluginType) + .def_property_readonly("plugin_version", &IPluginV2::getPluginVersion) + .def_property("plugin_namespace", &IPluginV2::getPluginNamespace, + py::cpp_function(&IPluginV2::setPluginNamespace, py::keep_alive<1, 2>{})) + .def("get_output_shape", lambdas::IPluginV2_get_output_shape, "index"_a, "input_shapes"_a, + IPluginV2Doc::get_output_shape) + .def("supports_format", &IPluginV2::supportsFormat, "dtype"_a, "format"_a, IPluginV2Doc::supports_format) + .def("configure_with_format", lambdas::IPluginV2_configure_with_format, "input_shapes"_a, "output_shapes"_a, + "dtype"_a, "format"_a, "max_batch_size"_a, IPluginV2Doc::configure_with_format) + .def("initialize", &IPluginV2::initialize, IPluginV2Doc::initialize) + .def("terminate", &IPluginV2::terminate, IPluginV2Doc::terminate) + .def("get_workspace_size", &IPluginV2::getWorkspaceSize, "max_batch_size"_a, IPluginV2Doc::get_workspace_size) + .def("execute_async", lambdas::IPluginV2_execute_async, "batch_size"_a, "inputs"_a, "outputs"_a, "workspace"_a, + "stream_handle"_a, IPluginV2Doc::execute_async) + .def_property_readonly("serialization_size", &IPluginV2::getSerializationSize) + .def( + "serialize", lambdas::IPluginV2_serialize, IPluginV2Doc::serialize, py::return_value_policy::take_ownership) + .def("destroy", &IPluginV2::destroy, IPluginV2Doc::destroy) + .def("clone", &IPluginV2::clone, IPluginV2Doc::clone); - // For IPluginV2 - static const auto IPluginV2_get_output_shape = [] (IPluginV2& self, int index, const std::vector inputShapes) { - return self.getOutputDimensions(index, inputShapes.data(), inputShapes.size()); - }; - - static const auto IPluginV2_configure_with_format = [] (IPluginV2& self, const std::vector inputShapes, const std::vector outputShapes, DataType dtype, TensorFormat format, int maxBatchSize) { - return self.configureWithFormat(inputShapes.data(), inputShapes.size(), outputShapes.data(), outputShapes.size(), dtype, format, maxBatchSize); - }; - - static const auto IPluginV2_serialize = [] (IPluginV2& self) { - size_t size = self.getSerializationSize(); - // Python will own and free the memory returned by this function - uint8_t* buffer = new uint8_t[size]; - self.serialize(buffer); - - py::buffer_info info{ - buffer, /* Pointer to buffer */ - sizeof(uint8_t), /* Size of one scalar */ - py::format_descriptor::format(), /* Python struct-style format descriptor */ - 1, /* Number of dimensions */ - { size }, /* Buffer dimensions */ - { sizeof(uint8_t) } /* Strides (in bytes) for each index */ - }; - - py::memoryview pyBuffer{info}; - return pyBuffer; - }; - - // `const vector::data()` corresponds to `const void* const*` (pointer to const-pointer to const void) - static const auto IPluginV2_execute_async = [] (IPluginV2& self, int batchSize, const std::vector& inputs, std::vector& outputs, void* workspace, long stream) { - return self.enqueue(batchSize, inputs.data(), outputs.data(), workspace, reinterpret_cast(stream)); - }; - - // For IPluginV2Ext - static const auto get_output_data_type = [] (IPluginV2Ext& self, int index, const std::vector inputTypes) { - return self.getOutputDataType(index, inputTypes.data(), inputTypes.size()); - }; - - // For IPluginV2Ext - makes copy of a vector as a bool[]. - static std::unique_ptr makeBoolArray(const std::vector& v) - { - int n = v.size(); - std::unique_ptr result(n > 0 ? new bool[n] : nullptr); - std::copy_n(v.begin(), n, result.get()); - return std::move(result); - } - - static const auto configure_plugin = [] (IPluginV2Ext& self, const std::vector inputShapes, const std::vector outputShapes, const std::vector inputTypes, const std::vector outputTypes, const std::vector inputIsBroadcasted, const std::vector outputIsBroadcasted, TensorFormat format, int maxBatchSize) { - auto inputBroadcast = makeBoolArray(inputIsBroadcasted); - auto outputBroadcast = makeBoolArray(outputIsBroadcasted); - return self.configurePlugin(inputShapes.data(), inputShapes.size(), outputShapes.data(), outputShapes.size(), inputTypes.data(), outputTypes.data(), inputBroadcast.get(), outputBroadcast.get(), format, maxBatchSize); - }; - - static const auto attach_to_context = [] (IPluginV2Ext& self, void* cudnn, void* cublas, void* allocator) { - self.attachToContext(static_cast(cudnn), static_cast(cublas), static_cast(allocator)); - }; - - // For PluginField - static const auto plugin_field_default_constructor = [] (const FallbackString& name) { - return new PluginField{name.c_str()}; - }; - - static const auto plugin_field_constructor = [] (const FallbackString& name, py::buffer& data, nvinfer1::PluginFieldType type) { - py::buffer_info info = data.request(); - // PluginField length is number of entries. type gives information about the size of each entry. - return new PluginField{name.c_str(), info.ptr, type, static_cast(info.size)}; - }; - - // For PluginFieldCollection - static const auto plugin_field_collection_constructor = [] (const std::vector& fields) { - return new PluginFieldCollection{static_cast(fields.size()), fields.data()}; - }; - - // For IPluginRegistry. We do an allocation here, but python takes ownership. - static const auto get_plugin_creator_list = [] (IPluginRegistry& self) { - int numCreators{0}; - IPluginCreator* const* ptr = self.getPluginCreatorList(&numCreators); - // This is NOT a memory leak - python will free when done. - return new std::vector(ptr, ptr + numCreators); - }; - - // For IPluginCreator - static const auto creator_create_plugin = [] (IPluginCreator& self, const std::string& name, const PluginFieldCollection* fc) { - return self.createPlugin(name.c_str(), fc); - }; - - static const auto deserialize_plugin = [] (IPluginCreator& self, const std::string& name, py::buffer& serializedPlugin) { - py::buffer_info info = serializedPlugin.request(); - return self.deserializePlugin(name.c_str(), info.ptr, info.size * info.itemsize); - }; - - // For IPluginFactory - static const auto factory_create_plugin = [] (IPluginFactory& self, const std::string& layerName, py::buffer& serializedPlugin) { - py::buffer_info info = serializedPlugin.request(); - return self.createPlugin(layerName.c_str(), info.ptr, info.size * info.itemsize); - }; - - } /* lambdas */ - - void bindPlugin(py::module& m) - { - py::class_>(m, "IPlugin", IPluginDoc::descr) - .def_property_readonly("num_outputs", &IPlugin::getNbOutputs) - .def("get_output_shape", lambdas::get_output_shape, "index"_a, "input_shapes"_a, IPluginDoc::get_output_shape) - .def("configure", lambdas::configure, "input_shapes"_a, "output_shapes"_a, "max_batch_size"_a, IPluginDoc::configure) - .def("initialize", &IPlugin::initialize, IPluginDoc::initialize) - .def("terminate", &IPlugin::terminate, IPluginDoc::terminate) - .def("get_workspace_size", &IPlugin::getWorkspaceSize, "max_batch_size"_a, IPluginDoc::get_workspace_size) - .def("execute_async", lambdas::execute_async, "batch_size"_a, "inputs"_a, "outputs"_a, "workspace"_a, "stream_handle"_a, IPluginDoc::execute_async) - .def_property_readonly("serialization_size", &IPlugin::getSerializationSize) - .def("serialize", &IPlugin::serialize, "buffer"_a, IPluginDoc::serialize) - ; - - py::class_>(m, "IPluginExt", IPluginExtDoc::descr) - .def_property_readonly("tensorrt_version", &IPluginExt::getTensorRTVersion) - .def("supports_format", &IPluginExt::supportsFormat, "dtype"_a, "format"_a, IPluginExtDoc::supports_format) - .def("configure_with_format", lambdas::configure_with_format, "input_shapes"_a, "output_shapes"_a, "dtype"_a, "format"_a, "max_batch_size"_a, IPluginExtDoc::configure_with_format) - ; - - py::class_>(m, "IPluginV2", IPluginV2Doc::descr) - .def_property_readonly("num_outputs", &IPluginV2::getNbOutputs) - .def_property_readonly("tensorrt_version", &IPluginV2::getTensorRTVersion) - .def_property_readonly("plugin_type", &IPluginV2::getPluginType) - .def_property_readonly("plugin_version", &IPluginV2::getPluginVersion) - .def_property("plugin_namespace", &IPluginV2::getPluginNamespace, py::cpp_function(&IPluginV2::setPluginNamespace, py::keep_alive<1, 2>{})) - .def("get_output_shape", lambdas::IPluginV2_get_output_shape, "index"_a, "input_shapes"_a, IPluginV2Doc::get_output_shape) - .def("supports_format", &IPluginV2::supportsFormat, "dtype"_a, "format"_a, IPluginV2Doc::supports_format) - .def("configure_with_format", lambdas::IPluginV2_configure_with_format, "input_shapes"_a, "output_shapes"_a, "dtype"_a, "format"_a, "max_batch_size"_a, IPluginV2Doc::configure_with_format) - .def("initialize", &IPluginV2::initialize, IPluginV2Doc::initialize) - .def("terminate", &IPluginV2::terminate, IPluginV2Doc::terminate) - .def("get_workspace_size", &IPluginV2::getWorkspaceSize, "max_batch_size"_a, IPluginV2Doc::get_workspace_size) - .def("execute_async", lambdas::IPluginV2_execute_async, "batch_size"_a, "inputs"_a, "outputs"_a, "workspace"_a, "stream_handle"_a, IPluginV2Doc::execute_async) - .def_property_readonly("serialization_size", &IPluginV2::getSerializationSize) - .def("serialize", lambdas::IPluginV2_serialize, IPluginV2Doc::serialize, py::return_value_policy::take_ownership) - .def("destroy", &IPluginV2::destroy, IPluginV2Doc::destroy) - .def("clone", &IPluginV2::clone, IPluginV2Doc::clone) - ; - - py::class_>(m, "IPluginV2Ext", IPluginV2ExtDoc::descr) - .def("get_output_data_type", lambdas::get_output_data_type, "index"_a, "input_types"_a, IPluginV2ExtDoc::get_output_data_type) - .def("configure_plugin", lambdas::configure_plugin, "input_shapes"_a, "output_shapes"_a, "input_types"_a, "output_types"_a, "input_is_broadcasted"_a, "output_is_broacasted"_a, "format"_a, "max_batch_size"_a, IPluginV2ExtDoc::configure_plugin) - .def("attach_to_context", lambdas::attach_to_context, "cudnn"_a, "cublas"_a, "allocator"_a, + py::class_(m, "IPluginV2Ext", IPluginV2ExtDoc::descr) + .def("get_output_data_type", lambdas::get_output_data_type, "index"_a, "input_types"_a, + IPluginV2ExtDoc::get_output_data_type) + .def("configure_plugin", lambdas::configure_plugin, "input_shapes"_a, "output_shapes"_a, "input_types"_a, + "output_types"_a, "input_is_broadcasted"_a, "output_is_broacasted"_a, "format"_a, "max_batch_size"_a, + IPluginV2ExtDoc::configure_plugin) + .def("attach_to_context", lambdas::attach_to_context, "cudnn"_a, "cublas"_a, "allocator"_a, IPluginV2ExtDoc::attach_to_context) - .def("detach_from_context", &IPluginV2Ext::detachFromContext, IPluginV2ExtDoc::detach_from_context) - .def("clone", &IPluginV2Ext::clone, IPluginV2ExtDoc::clone); - ; + .def("detach_from_context", &IPluginV2Ext::detachFromContext, IPluginV2ExtDoc::detach_from_context) + .def("clone", &IPluginV2Ext::clone, IPluginV2ExtDoc::clone); + ; - py::enum_(m, "PluginFieldType", PluginFieldTypeDoc::descr) - .value("FLOAT16", PluginFieldType::kFLOAT16) - .value("FLOAT32", PluginFieldType::kFLOAT32) - .value("FLOAT64", PluginFieldType::kFLOAT64) - .value("INT8", PluginFieldType::kINT8) - .value("INT16", PluginFieldType::kINT16) - .value("INT32", PluginFieldType::kINT32) - .value("CHAR", PluginFieldType::kCHAR) - .value("DIMS", PluginFieldType::kDIMS) - .value("UNKNOWN", PluginFieldType::kUNKNOWN) - ; + py::enum_(m, "PluginFieldType", PluginFieldTypeDoc::descr) + .value("FLOAT16", PluginFieldType::kFLOAT16) + .value("FLOAT32", PluginFieldType::kFLOAT32) + .value("FLOAT64", PluginFieldType::kFLOAT64) + .value("INT8", PluginFieldType::kINT8) + .value("INT16", PluginFieldType::kINT16) + .value("INT32", PluginFieldType::kINT32) + .value("CHAR", PluginFieldType::kCHAR) + .value("DIMS", PluginFieldType::kDIMS) + .value("UNKNOWN", PluginFieldType::kUNKNOWN); - py::class_(m, "PluginField", PluginFieldDoc::descr) - .def(py::init(lambdas::plugin_field_default_constructor), "name"_a = "", py::keep_alive<1, 2>{}) - .def(py::init(lambdas::plugin_field_constructor), "name"_a, "data"_a, "type"_a = nvinfer1::PluginFieldType::kUNKNOWN, py::keep_alive<1, 2>{}, py::keep_alive<1, 3>{}) - .def_property("name", [] (PluginField& self) { return self.name; }, py::cpp_function([] (PluginField& self, FallbackString& name) { self.name = name.c_str(); }, py::keep_alive<1, 2>{})) - .def_property("data", [] (PluginField& self) { return self.data; }, py::cpp_function([] (PluginField& self, py::buffer& buffer) { py::buffer_info info = buffer.request(); self.data = info.ptr; }, py::keep_alive<1, 2>{})) - .def_readwrite("type", &PluginField::type) - .def_readwrite("size", &PluginField::length) - ; + py::class_(m, "PluginField", PluginFieldDoc::descr) + .def(py::init(lambdas::plugin_field_default_constructor), "name"_a = "", py::keep_alive<1, 2>{}) + .def(py::init(lambdas::plugin_field_constructor), "name"_a, "data"_a, + "type"_a = nvinfer1::PluginFieldType::kUNKNOWN, py::keep_alive<1, 2>{}, py::keep_alive<1, 3>{}) + .def_property("name", [](PluginField& self) { return self.name; }, + py::cpp_function( + [](PluginField& self, FallbackString& name) { self.name = name.c_str(); }, py::keep_alive<1, 2>{})) + .def_property("data", [](PluginField& self) { return self.data; }, + py::cpp_function( + [](PluginField& self, py::buffer& buffer) { + py::buffer_info info = buffer.request(); + self.data = info.ptr; + }, + py::keep_alive<1, 2>{})) + .def_readwrite("type", &PluginField::type) + .def_readwrite("size", &PluginField::length); - // PluginFieldCollection behaves like an iterable, and can be constructed from iterables. - py::class_(m, "PluginFieldCollection_", PluginFieldCollectionDoc::descr) - .def(py::init<>(lambdas::plugin_field_collection_constructor), py::keep_alive<1, 2>{}) - .def("__len__", [] (PluginFieldCollection& self) { return self.nbFields; }) - .def("__getitem__", [] (PluginFieldCollection& self, int index) { if (index >= self.nbFields) throw py::index_error(); return self.fields[index]; }) - ; + // PluginFieldCollection behaves like an iterable, and can be constructed from iterables. + py::class_(m, "PluginFieldCollection_", PluginFieldCollectionDoc::descr) + .def(py::init<>(lambdas::plugin_field_collection_constructor), py::keep_alive<1, 2>{}) + .def("__len__", [](PluginFieldCollection& self) { return self.nbFields; }) + .def("__getitem__", [](PluginFieldCollection& self, int index) { + if (index >= self.nbFields) + throw py::index_error(); + return self.fields[index]; + }); - // Creating a trt.PluginFieldCollection in Python will actually construct a vector, - // which can then be converted to an actual C++ PluginFieldCollection. - py::implicitly_convertible, PluginFieldCollection>(); + // Creating a trt.PluginFieldCollection in Python will actually construct a vector, + // which can then be converted to an actual C++ PluginFieldCollection. + py::implicitly_convertible, PluginFieldCollection>(); - py::class_(m, "IPluginCreator", IPluginCreatorDoc::descr) - .def_property_readonly("tensorrt_version", &IPluginCreator::getTensorRTVersion) - .def_property_readonly("name", &IPluginCreator::getPluginName) - .def_property_readonly("plugin_version", &IPluginCreator::getPluginVersion) - .def_property_readonly("field_names", &IPluginCreator::getFieldNames) - .def_property("plugin_namespace", &IPluginCreator::getPluginNamespace, py::cpp_function(&IPluginCreator::setPluginNamespace, py::keep_alive<1, 2>{})) - .def("create_plugin", lambdas::creator_create_plugin, "name"_a, "field_collection"_a, IPluginCreatorDoc::create_plugin) - .def("deserialize_plugin", lambdas::deserialize_plugin, "name"_a, "serialized_plugin"_a, IPluginCreatorDoc::deserialize_plugin) - ; + py::class_(m, "IPluginCreator", IPluginCreatorDoc::descr) + .def_property_readonly("tensorrt_version", &IPluginCreator::getTensorRTVersion) + .def_property_readonly("name", &IPluginCreator::getPluginName) + .def_property_readonly("plugin_version", &IPluginCreator::getPluginVersion) + .def_property_readonly("field_names", lambdas::get_field_names, py::return_value_policy::reference_internal) + .def_property("plugin_namespace", &IPluginCreator::getPluginNamespace, + py::cpp_function(&IPluginCreator::setPluginNamespace, py::keep_alive<1, 2>{})) + .def("create_plugin", lambdas::creator_create_plugin, "name"_a, "field_collection"_a, + IPluginCreatorDoc::create_plugin) + .def("deserialize_plugin", lambdas::deserialize_plugin, "name"_a, "serialized_plugin"_a, + IPluginCreatorDoc::deserialize_plugin); - py::class_>(m, "IPluginRegistry", IPluginRegistryDoc::descr) - // Tell python that it should free this when done. - .def_property_readonly("plugin_creator_list", lambdas::get_plugin_creator_list) - .def("register_creator", &IPluginRegistry::registerCreator, "creator"_a, "plugin_namespace"_a = "", py::keep_alive<1, 2>{}, IPluginRegistryDoc::register_creator) - .def("get_plugin_creator", &IPluginRegistry::getPluginCreator, "type"_a, "version"_a, "plugin_namespace"_a = "", py::return_value_policy::reference_internal, IPluginRegistryDoc::get_plugin_creator) - ; + py::class_>( + m, "IPluginRegistry", IPluginRegistryDoc::descr) + .def_property_readonly("plugin_creator_list", lambdas::get_plugin_creator_list) + .def("register_creator", &IPluginRegistry::registerCreator, "creator"_a, "plugin_namespace"_a = "", + py::keep_alive<1, 2>{}, IPluginRegistryDoc::register_creator) + .def("deregister_creator", &IPluginRegistry::deregisterCreator, "creator"_a, + IPluginRegistryDoc::deregister_creator) + .def("get_plugin_creator", &IPluginRegistry::getPluginCreator, "type"_a, "version"_a, "plugin_namespace"_a = "", + py::return_value_policy::reference_internal, IPluginRegistryDoc::get_plugin_creator) + .def_property("error_recorder", &IPluginRegistry::getErrorRecorder, + py::cpp_function(&IPluginRegistry::setErrorRecorder, py::keep_alive<1, 2>{})); - py::class_(m, "IPluginFactory", IPluginFactoryDoc::descr) - .def("create_plugin", lambdas::factory_create_plugin, "layer_name"_a, "serialized_plugin"_a, IPluginFactoryDoc::create_plugin) - ; + m.def("get_plugin_registry", &getPluginRegistry, py::return_value_policy::reference, + FreeFunctionsDoc::get_plugin_registry); + m.def("init_libnvinfer_plugins", &initLibNvInferPlugins, "logger"_a, "namespace"_a, + FreeFunctionsDoc::init_libnvinfer_plugins); - m.def("get_plugin_registry", &getPluginRegistry, py::return_value_policy::reference, FreeFunctionsDoc::get_plugin_registry); - m.def("init_libnvinfer_plugins", &initLibNvInferPlugins, "logger"_a, "namespace"_a, FreeFunctionsDoc::init_libnvinfer_plugins); - - } // Plugin -} /* tensorrt */ +} // Plugin +} // namespace tensorrt diff --git a/python/src/parsers/pyCaffe.cpp b/python/src/parsers/pyCaffe.cpp index 4368537e..d6511ed4 100644 --- a/python/src/parsers/pyCaffe.cpp +++ b/python/src/parsers/pyCaffe.cpp @@ -15,85 +15,74 @@ */ // Implementation of PyBind11 Binding Code for CaffeParser -#include "NvCaffeParser.h" -#include "NvInfer.h" -#include "utils.h" -#include "parsers/pyCaffeDoc.h" #include "ForwardDeclarations.h" -// For py::array +#include "parsers/pyCaffeDoc.h" +#include "utils.h" #include namespace tensorrt { - using namespace nvcaffeparser1; +using namespace nvcaffeparser1; - namespace lambdas - { - static const auto create_plugin = [] (IPluginFactory& self, const std::string& layerName, const std::vector& weights) { - return self.createPlugin(layerName.c_str(), weights.data(), weights.size()); - }; +namespace lambdas +{ +static const auto parse_binary_proto = [](ICaffeParser& self, const std::string& filename) { + using VoidFunc = void (*)(void*); - static const auto parse_binary_proto = [] (ICaffeParser& self, const std::string& filename) { - using VoidFunc = void (*)(void*); + // Type-erasure allows us to properly destroy the IBinaryProtoBlob in the bindings. + nvcaffeparser1::IBinaryProtoBlob* proto = self.parseBinaryProto(filename.c_str()); + VoidFunc freeFunc = [](void* p) { static_cast(p)->destroy(); }; + py::capsule freeBlob{static_cast(proto), freeFunc}; - // Type-erasure allows us to properly destroy the IBinaryProtoBlob in the bindings. - nvcaffeparser1::IBinaryProtoBlob* proto = self.parseBinaryProto(filename.c_str()); - VoidFunc freeFunc = [](void* p) { static_cast(p) -> destroy(); }; - py::capsule freeBlob{static_cast(proto), freeFunc}; + // By specifying the py::capsule as a parent here, we tie the lifetime of the data buffer to this array. + // When this array is eventually destroyed on the Python side, the capsule parent will free(protoPtr). + return py::array{ + utils::nptype(proto->getDataType()), utils::volume(proto->getDimensions()), proto->getData(), freeBlob}; +}; - // By specifying the py::capsule as a parent here, we tie the lifetime of the data buffer to this array. - // When this array is eventually destroyed on the Python side, the capsule parent will free(protoPtr). - return py::array{utils::nptype(proto->getDataType()), utils::volume(proto->getDimensions()), proto->getData(), freeBlob}; - }; +static const auto parse_buffer = [](ICaffeParser& self, py::buffer& deploy, py::buffer& model, + nvinfer1::INetworkDefinition& network, nvinfer1::DataType dtype) { + py::buffer_info deploy_info = deploy.request(); + py::buffer_info model_info = model.request(); + return self.parseBuffers(static_cast(deploy_info.ptr), deploy_info.size * deploy_info.itemsize, + static_cast(model_info.ptr), model_info.size * model_info.itemsize, network, dtype); +}; - static const auto parse_buffer = [] (ICaffeParser& self, py::buffer& deploy, py::buffer& model, - nvinfer1::INetworkDefinition& network, nvinfer1::DataType dtype) { - py::buffer_info deploy_info = deploy.request(); - py::buffer_info model_info = model.request(); - return self.parseBuffers(static_cast(deploy_info.ptr), deploy_info.size * deploy_info.itemsize, - static_cast(model_info.ptr), model_info.size * model_info.itemsize, network, dtype); - }; +// For IPluginFactoryV2 +static const auto PluginV2_create_plugin + = [](IPluginFactoryV2& self, const std::string& layerName, const std::vector& weights) { + return self.createPlugin(layerName.c_str(), weights.data(), weights.size()); + }; +} // namespace lambdas - // For IPluginFactoryV2 - static const auto PluginV2_create_plugin = [] (IPluginFactoryV2& self, const std::string& layerName, const std::vector& weights) { - return self.createPlugin(layerName.c_str(), weights.data(), weights.size()); - }; - } /* lambdas */ +void bindCaffe(py::module& m) +{ + py::class_>( + m, "IBlobNameToTensor", IBlobNameToTensorDoc::descr) + .def("find", &IBlobNameToTensor::find, "name"_a, IBlobNameToTensorDoc::find); - void bindCaffe(py::module& m) - { - py::class_ >(m, "IBlobNameToTensor", IBlobNameToTensorDoc::descr) - .def ("find", &IBlobNameToTensor::find, "name"_a, IBlobNameToTensorDoc::find) - ; + py::class_(m, "ICaffePluginFactoryV2", ICaffePluginFactoryV2Doc::descr) + .def("is_plugin_v2", &IPluginFactoryV2::isPluginV2, "layer_name"_a, ICaffePluginFactoryV2Doc::is_plugin_v2) + .def("create_plugin", lambdas::PluginV2_create_plugin, "layer_name"_a, "weights"_a, py::keep_alive<1, 3>{}, + ICaffePluginFactoryV2Doc::create_plugin); - py::class_(m, "ICaffePluginFactory", ICaffePluginFactoryDoc::descr) - .def("is_plugin", &IPluginFactory::isPlugin, "layer_name"_a, ICaffePluginFactoryDoc::is_plugin) - .def("create_plugin", lambdas::create_plugin, "layer_name"_a, "weights"_a, py::keep_alive<1, 3>{}, ICaffePluginFactoryDoc::create_plugin) - ; + py::class_>(m, "CaffeParser", ICaffeParserDoc::descr) + .def(py::init(&nvcaffeparser1::createCaffeParser)) + .def_property("protobuf_buffer_size", nullptr, &ICaffeParser::setProtobufBufferSize) + .def_property( + "plugin_factory_v2", nullptr, py::cpp_function(&ICaffeParser::setPluginFactoryV2, py::keep_alive<1, 2>{})) + .def_property( + "plugin_namespace", nullptr, py::cpp_function(&ICaffeParser::setPluginNamespace, py::keep_alive<1, 2>{})) + .def("parse", &ICaffeParser::parse, "deploy"_a, "model"_a, "network"_a, "dtype"_a, ICaffeParserDoc::parse, + py::keep_alive<4, 1>{}) + .def("parse_buffer", lambdas::parse_buffer, "deploy_buffer"_a, "model_buffer"_a, "network"_a, "dtype"_a, + ICaffeParserDoc::parse_buffer, py::keep_alive<4, 1>{}) + .def("parse_binary_proto", lambdas::parse_binary_proto, "filename"_a, ICaffeParserDoc::parse_binary_proto) + .def_property("error_recorder", &ICaffeParser::getErrorRecorder, + py::cpp_function(&ICaffeParser::setErrorRecorder, py::keep_alive<1, 2>{})) + .def("__del__", &utils::doNothingDel); - py::class_(m, "ICaffePluginFactoryExt", ICaffePluginFactoryExtDoc::descr) - .def("get_version", &IPluginFactoryExt::getVersion, ICaffePluginFactoryExtDoc::get_version) - .def("is_plugin_ext", &IPluginFactoryExt::isPluginExt, "layer_name"_a, ICaffePluginFactoryExtDoc::is_plugin_ext) - ; - - py::class_(m, "ICaffePluginFactoryV2", ICaffePluginFactoryV2Doc::descr) - .def("is_plugin_v2", &IPluginFactoryV2::isPluginV2, "layer_name"_a, ICaffePluginFactoryV2Doc::is_plugin_v2) - .def("create_plugin", lambdas::PluginV2_create_plugin, "layer_name"_a, "weights"_a, py::keep_alive<1, 3>{}, ICaffePluginFactoryV2Doc::create_plugin) - ; - - py::class_ >(m, "CaffeParser", ICaffeParserDoc::descr) - .def(py::init(&nvcaffeparser1::createCaffeParser)) - .def_property("protobuf_buffer_size", nullptr, &ICaffeParser::setProtobufBufferSize) - .def_property("plugin_factory", nullptr, py::cpp_function(&ICaffeParser::setPluginFactory, py::keep_alive<1, 2>{})) - .def_property("plugin_factory_ext", nullptr, py::cpp_function(&ICaffeParser::setPluginFactoryExt, py::keep_alive<1, 2>{})) - .def_property("plugin_factory_v2", nullptr, py::cpp_function(&ICaffeParser::setPluginFactoryV2, py::keep_alive<1, 2>{})) - .def_property("plugin_namespace", nullptr, py::cpp_function(&ICaffeParser::setPluginNamespace, py::keep_alive<1, 2>{})) - .def("parse", &ICaffeParser::parse, "deploy"_a, "model"_a, "network"_a, "dtype"_a, ICaffeParserDoc::parse) - .def("parse_buffer", lambdas::parse_buffer, "deploy_buffer"_a, "model_buffer"_a, "network"_a, "dtype"_a, ICaffeParserDoc::parse_buffer) - .def("parse_binary_proto", lambdas::parse_binary_proto, "filename"_a, ICaffeParserDoc::parse_binary_proto) - .def("__del__", &ICaffeParser::destroy) - ; - - m.def("shutdown_protobuf_library", &nvcaffeparser1::shutdownProtobufLibrary, FreeFunctionsDoc::shutdown_protobuf_library); - } + m.def("shutdown_protobuf_library", &nvcaffeparser1::shutdownProtobufLibrary, + FreeFunctionsDoc::shutdown_protobuf_library); } +} // namespace tensorrt diff --git a/python/src/parsers/pyOnnx.cpp b/python/src/parsers/pyOnnx.cpp index ea99ee4c..39a04469 100644 --- a/python/src/parsers/pyOnnx.cpp +++ b/python/src/parsers/pyOnnx.cpp @@ -15,117 +15,105 @@ */ // Implementation of PyBind11 Binding Code for OnnxParser -#include "NvOnnxParser.h" #include "ForwardDeclarations.h" #include "parsers/pyOnnxDoc.h" +#include "utils.h" #include using namespace nvonnxparser; namespace tensorrt { +// Long lambda functions should go here rather than being inlined into the bindings (1 liners are OK). namespace lambdas { - static const auto error_code_str = [] (ErrorCode self) { - switch (self) { - case ErrorCode::kSUCCESS: - return "SUCCESS"; - case ErrorCode::kINTERNAL_ERROR: - return "INTERNAL_ERROR"; - case ErrorCode::kMEM_ALLOC_FAILED: - return "MEM_ALLOC_FAILED"; - case ErrorCode::kMODEL_DESERIALIZE_FAILED: - return "MODEL_DESERIALIZE_FAILED"; - case ErrorCode::kINVALID_VALUE: - return "INVALID_VALUE"; - case ErrorCode::kINVALID_GRAPH: - return "INVALID_GRAPH"; - case ErrorCode::kINVALID_NODE: - return "INVALID_NODE"; - case ErrorCode::kUNSUPPORTED_GRAPH: - return "UNSUPPORTED_GRAPH"; - case ErrorCode::kUNSUPPORTED_NODE: - return "UNSUPPORTED_NODE"; - } - return "UNKNOWN"; - }; - - static const auto parser_error_str = [](IParserError& self) { - return "In node " + std::to_string(self.node()) + " (" + self.func() + "): " + error_code_str(self.code()) + ": " + self.desc(); - }; - - // For ONNX Parser - static const auto parse = [](IParser& self, const py::buffer& model, const char* path = nullptr) { - py::buffer_info info = model.request(); - return self.parse(info.ptr, info.size * info.itemsize, path); - }; - - static const auto parseFromFile - = [](IParser& self, const std::string& model) { return self.parseFromFile(model.c_str(), 0); - }; - - static const auto getRefitMap = [] (IParser& self) +static const auto error_code_str = [](ErrorCode self) { + switch (self) { - int size = self.getRefitMap(nullptr, nullptr, nullptr); - std::vector weightNames(size); - std::vector layerNames(size); - std::vector roles(size); - self.getRefitMap(weightNames.data(), layerNames.data(), roles.data()); - return std::tuple, std::vector, std::vector>{weightNames, layerNames, roles}; - }; + case ErrorCode::kSUCCESS: return "SUCCESS"; + case ErrorCode::kINTERNAL_ERROR: return "INTERNAL_ERROR"; + case ErrorCode::kMEM_ALLOC_FAILED: return "MEM_ALLOC_FAILED"; + case ErrorCode::kMODEL_DESERIALIZE_FAILED: return "MODEL_DESERIALIZE_FAILED"; + case ErrorCode::kINVALID_VALUE: return "INVALID_VALUE"; + case ErrorCode::kINVALID_GRAPH: return "INVALID_GRAPH"; + case ErrorCode::kINVALID_NODE: return "INVALID_NODE"; + case ErrorCode::kUNSUPPORTED_GRAPH: return "UNSUPPORTED_GRAPH"; + case ErrorCode::kUNSUPPORTED_NODE: return "UNSUPPORTED_NODE"; + } + return "UNKNOWN"; +}; - static const auto supportsModel = [](IParser& self, const py::buffer& model, const char* path = nullptr) { - py::buffer_info info = model.request(); - SubGraphCollection_t subgraphs; - const bool supported = self.supportsModel(info.ptr, info.size * info.itemsize, subgraphs, path); - return std::make_pair(supported, subgraphs); - }; +static const auto parser_error_str = [](IParserError& self) { + return "In node " + std::to_string(self.node()) + " (" + self.func() + "): " + error_code_str(self.code()) + ": " + + self.desc(); +}; + +static const auto parse = [](IParser& self, const py::buffer& model, const char* path = nullptr) { + py::buffer_info info = model.request(); + return self.parse(info.ptr, info.size * info.itemsize, path); +}; + +static const auto parse_with_weight_descriptors = [](IParser& self, const py::buffer& model) { + py::buffer_info info = model.request(); + return self.parseWithWeightDescriptors(info.ptr, info.size * info.itemsize); +}; + +static const auto parseFromFile + = [](IParser& self, const std::string& model) { return self.parseFromFile(model.c_str(), 0); }; + +static const auto supportsModel = [](IParser& self, const py::buffer& model, const char* path = nullptr) { + py::buffer_info info = model.request(); + SubGraphCollection_t subgraphs; + const bool supported = self.supportsModel(info.ptr, info.size * info.itemsize, subgraphs, path); + return std::make_pair(supported, subgraphs); +}; } // namespace lambdas - void bindOnnx(py::module& m) - { - py::bind_vector>(m, "NodeIndices"); - py::bind_vector(m, "SubGraphCollection"); +void bindOnnx(py::module& m) +{ + py::bind_vector>(m, "NodeIndices"); + py::bind_vector(m, "SubGraphCollection"); - py::class_>(m, "OnnxParser", OnnxParserDoc::descr) - .def(py::init(&nvonnxparser::createParser), "network"_a, "logger"_a, OnnxParserDoc::init) - .def("parse", lambdas::parse, "model"_a, "path"_a = nullptr, OnnxParserDoc::parse, - py::call_guard{}) - .def("parse_from_file", lambdas::parseFromFile, "model"_a, OnnxParserDoc::parseFromFile, - py::call_guard{}) - .def("supports_operator", &IParser::supportsOperator, "op_name"_a, OnnxParserDoc::supports_operator) - .def("supports_model", lambdas::supportsModel, "model"_a, "path"_a = nullptr, - OnnxParserDoc::supports_model) - .def_property_readonly("num_errors", &IParser::getNbErrors) - .def("get_error", &IParser::getError, "index"_a, OnnxParserDoc::get_error) - .def("clear_errors", &IParser::clearErrors, OnnxParserDoc::clear_errors) - .def("get_refit_map", lambdas::getRefitMap, OnnxParserDoc::get_refit_map) - .def("__del__", &IParser::destroy); + py::class_(m, "OnnxParser", OnnxParserDoc::descr) + .def(py::init(&nvonnxparser::createParser), "network"_a, "logger"_a, OnnxParserDoc::init, + py::keep_alive<1, 2>{}, py::keep_alive<1, 3>{}, py::keep_alive<2, 1>{}) + .def("parse", lambdas::parse, "model"_a, "path"_a = nullptr, OnnxParserDoc::parse, + py::call_guard{}) + .def("parse_with_weight_descriptors", lambdas::parse_with_weight_descriptors, "model"_a, + OnnxParserDoc::parse_with_weight_descriptors, py::call_guard{}) + .def("parse_from_file", lambdas::parseFromFile, "model"_a, OnnxParserDoc::parse_from_file, + py::call_guard{}) + .def("supports_operator", &IParser::supportsOperator, "op_name"_a, OnnxParserDoc::supports_operator) + .def("supports_model", lambdas::supportsModel, "model"_a, "path"_a = nullptr, OnnxParserDoc::supports_model) + .def_property_readonly("num_errors", &IParser::getNbErrors) + .def("get_error", &IParser::getError, "index"_a, OnnxParserDoc::get_error) + .def("clear_errors", &IParser::clearErrors, OnnxParserDoc::clear_errors) + .def("__del__", &utils::doNothingDel); - py::enum_(m, "ErrorCode", ErrorCodeDoc::descr) - .value("SUCCESS", ErrorCode::kSUCCESS) - .value("INTERNAL_ERROR", ErrorCode::kINTERNAL_ERROR) - .value("MEM_ALLOC_FAILED", ErrorCode::kMEM_ALLOC_FAILED) - .value("MODEL_DESERIALIZE_FAILED", ErrorCode::kMODEL_DESERIALIZE_FAILED) - .value("INVALID_VALUE", ErrorCode::kINVALID_VALUE) - .value("INVALID_GRAPH", ErrorCode::kINVALID_GRAPH) - .value("INVALID_NODE", ErrorCode::kINVALID_NODE) - .value("UNSUPPORTED_GRAPH", ErrorCode::kUNSUPPORTED_GRAPH) - .value("UNSUPPORTED_NODE", ErrorCode::kUNSUPPORTED_NODE) - .def("__str__", lambdas::error_code_str) - .def("__repr__", lambdas::error_code_str); + py::enum_(m, "ErrorCode", ErrorCodeDoc::descr) + .value("SUCCESS", ErrorCode::kSUCCESS) + .value("INTERNAL_ERROR", ErrorCode::kINTERNAL_ERROR) + .value("MEM_ALLOC_FAILED", ErrorCode::kMEM_ALLOC_FAILED) + .value("MODEL_DESERIALIZE_FAILED", ErrorCode::kMODEL_DESERIALIZE_FAILED) + .value("INVALID_VALUE", ErrorCode::kINVALID_VALUE) + .value("INVALID_GRAPH", ErrorCode::kINVALID_GRAPH) + .value("INVALID_NODE", ErrorCode::kINVALID_NODE) + .value("UNSUPPORTED_GRAPH", ErrorCode::kUNSUPPORTED_GRAPH) + .value("UNSUPPORTED_NODE", ErrorCode::kUNSUPPORTED_NODE) + .def("__str__", lambdas::error_code_str) + .def("__repr__", lambdas::error_code_str); - py::class_>(m, "ParserError") - .def("code", &IParserError::code, ParserErrorDoc::code) - .def("desc", &IParserError::desc, ParserErrorDoc::desc) - .def("file", &IParserError::file, ParserErrorDoc::file) - .def("line", &IParserError::line, ParserErrorDoc::line) - .def("func", &IParserError::func, ParserErrorDoc::func) - .def("node", &IParserError::node, ParserErrorDoc::node) - .def("__str__", lambdas::parser_error_str) - .def("__repr__", lambdas::parser_error_str); + py::class_>(m, "ParserError") + .def("code", &IParserError::code, ParserErrorDoc::code) + .def("desc", &IParserError::desc, ParserErrorDoc::desc) + .def("file", &IParserError::file, ParserErrorDoc::file) + .def("line", &IParserError::line, ParserErrorDoc::line) + .def("func", &IParserError::func, ParserErrorDoc::func) + .def("node", &IParserError::node, ParserErrorDoc::node) + .def("__str__", lambdas::parser_error_str) + .def("__repr__", lambdas::parser_error_str); - // Free functions. - m.def("get_nv_onnx_parser_version", &getNvOnnxParserVersion, get_nv_onnx_parser_version); - } + // Free functions. + m.def("get_nv_onnx_parser_version", &getNvOnnxParserVersion, get_nv_onnx_parser_version); +} } // namespace tensorrt diff --git a/python/src/parsers/pyUff.cpp b/python/src/parsers/pyUff.cpp index 6e6c386f..4b5aa896 100644 --- a/python/src/parsers/pyUff.cpp +++ b/python/src/parsers/pyUff.cpp @@ -15,95 +15,65 @@ */ // Implementation of PyBind11 Binding Code for UffParser -#include "NvUffParser.h" -#include "NvInfer.h" -#include "parsers/pyUffDoc.h" #include "ForwardDeclarations.h" +#include "parsers/pyUffDoc.h" +#include "utils.h" namespace tensorrt { - using namespace nvuffparser; +using namespace nvuffparser; - namespace lambdas { - static const auto create_plugin = [] (IPluginFactory& self, const std::string& layerName, const std::vector& weights, const FieldCollection& fc) { - return self.createPlugin(layerName.c_str(), weights.data(), weights.size(), fc); - }; +namespace lambdas +{ +static const auto uff_parse_buffer = [](IUffParser& self, py::buffer& buffer, nvinfer1::INetworkDefinition& network, + nvinfer1::DataType weightsType = nvinfer1::DataType::kFLOAT) { + py::buffer_info info = buffer.request(); + return self.parseBuffer(static_cast(info.ptr), info.size * info.itemsize, network, weightsType); +}; +} // namespace lambdas - static const auto uff_parse_buffer = [] (IUffParser& self, py::buffer& buffer, nvinfer1::INetworkDefinition& network, nvinfer1::DataType weightsType = nvinfer1::DataType::kFLOAT) { - py::buffer_info info = buffer.request(); - return self.parseBuffer(static_cast(info.ptr), info.size * info.itemsize, network, weightsType); - }; - } /* lambdas */ +void bindUff(py::module& m) +{ + py::enum_(m, "UffInputOrder", UffInputOrderDoc::descr) + .value("NCHW", UffInputOrder::kNCHW) + .value("NHWC", UffInputOrder::kNHWC) + .value("NC", UffInputOrder::kNC); - void bindUff(py::module& m) - { - py::enum_(m, "UffInputOrder", UffInputOrderDoc::descr) - .value("NCHW", UffInputOrder::kNCHW) - .value("NHWC", UffInputOrder::kNHWC) - .value("NC", UffInputOrder::kNC) - ; + py::enum_(m, "FieldType", FieldTypeDoc::descr) + .value("FLOAT", FieldType::kFLOAT) + .value("INT32", FieldType::kINT32) + .value("CHAR", FieldType::kCHAR) + .value("DIMS", FieldType::kDIMS) + .value("DATATYPE", FieldType::kDATATYPE) + .value("UNKNOWN", FieldType::kUNKNOWN); - py::enum_(m, "FieldType", FieldTypeDoc::descr) - .value("FLOAT", FieldType::kFLOAT) - .value("INT32", FieldType::kINT32) - .value("CHAR", FieldType::kCHAR) - .value("DIMS", FieldType::kDIMS) - .value("DATATYPE", FieldType::kDATATYPE) - .value("UNKNOWN", FieldType::kUNKNOWN ) - ; + py::class_(m, "FieldMap", FieldMapDoc::descr) + .def(py::init(), "name"_a, "data"_a, "type"_a, "length"_a = 1) + .def_readwrite("name", &FieldMap::name) + .def_readwrite("data", &FieldMap::data) + .def_readwrite("type", &FieldMap::type) + .def_readwrite("length", &FieldMap::length); - py::class_(m, "FieldMap", FieldMapDoc::descr) - .def(py::init(), "name"_a, "data"_a, "type"_a, "length"_a = 1) - .def_readwrite("name", &FieldMap::name) - .def_readwrite("data", &FieldMap::data) - .def_readwrite("type", &FieldMap::type) - .def_readwrite("length", &FieldMap::length) - ; + py::class_(m, "FieldCollection", FieldCollectionDoc::descr) + .def_readwrite("num_fields", &FieldCollection::nbFields) + .def_readwrite("fields", &FieldCollection::fields); - py::class_(m, "FieldCollection", FieldCollectionDoc::descr) - .def_readwrite("num_fields", &FieldCollection::nbFields) - .def_readwrite("fields", &FieldCollection::fields) - ; - - class pyUffIPluginFactory : public IPluginFactory - { - using IPluginFactory::IPluginFactory; - - bool isPlugin(const char* layerName) override - { - PYBIND11_OVERLOAD_PURE_NAME(bool, IPluginFactory, "is_plugin", isPlugin, layerName); - } - - nvinfer1::IPlugin* createPlugin(const char* layerName, const nvinfer1::Weights* weights, int nbWeights, const FieldCollection fc) override - { - PYBIND11_OVERLOAD_PURE_NAME(nvinfer1::IPlugin*, IPluginFactory, "create_plugin", createPlugin, layerName, weights, nbWeights, fc); - } - }; - - py::class_(m, "IUffPluginFactory", IUffPluginFactoryDoc::descr) - .def(py::init<>()) - .def("is_plugin", &IPluginFactory::isPlugin, "layer_name"_a, IUffPluginFactoryDoc::is_plugin) - .def("create_plugin", lambdas::create_plugin, "layer_name"_a, "weights"_a, "field_collection"_a, IUffPluginFactoryDoc::create_plugin) - ; - - py::class_(m, "IUffPluginFactoryExt", IUffPluginFactoryExtDoc::descr) - .def("get_version", &IPluginFactoryExt::getVersion, IUffPluginFactoryExtDoc::get_version) - .def("is_plugin_ext", &IPluginFactoryExt::isPluginExt, "layer_name"_a, IUffPluginFactoryExtDoc::is_plugin_ext) - ; - - py::class_ >(m, "UffParser", UffParserDoc::descr) - .def(py::init(&createUffParser)) - .def_property_readonly("uff_required_version_major", &IUffParser::getUffRequiredVersionMajor) - .def_property_readonly("uff_required_version_minor", &IUffParser::getUffRequiredVersionMinor) - .def_property_readonly("uff_required_version_patch", &IUffParser::getUffRequiredVersionPatch) - .def_property("plugin_factory", nullptr, py::cpp_function(&IUffParser::setPluginFactory, py::keep_alive<1, 2>{})) - .def_property("plugin_factory_ext", nullptr, py::cpp_function(&IUffParser::setPluginFactoryExt, py::keep_alive<1, 2>{})) - .def_property("plugin_namespace", nullptr, py::cpp_function(&IUffParser::setPluginNamespace, py::keep_alive<1, 2>{})) - .def("register_input", &IUffParser::registerInput, "name"_a, "shape"_a, "order"_a = UffInputOrder::kNCHW, UffParserDoc::register_input) - .def("register_output", &IUffParser::registerOutput, "name"_a, UffParserDoc::register_output) - .def("parse", &IUffParser::parse, "file"_a, "network"_a, "weights_type"_a = nvinfer1::DataType::kFLOAT, UffParserDoc::parse) - .def("parse_buffer", lambdas::uff_parse_buffer, "buffer"_a, "network"_a, "weights_type"_a = nvinfer1::DataType::kFLOAT, UffParserDoc::parse_buffer) - .def("__del__", &IUffParser::destroy) - ; - } -} /* tensorrt */ + py::class_>(m, "UffParser", UffParserDoc::descr) + .def(py::init(&createUffParser)) + .def_property_readonly("uff_required_version_major", &IUffParser::getUffRequiredVersionMajor) + .def_property_readonly("uff_required_version_minor", &IUffParser::getUffRequiredVersionMinor) + .def_property_readonly("uff_required_version_patch", &IUffParser::getUffRequiredVersionPatch) + .def_property( + "plugin_namespace", nullptr, py::cpp_function(&IUffParser::setPluginNamespace, py::keep_alive<1, 2>{})) + .def("register_input", &IUffParser::registerInput, "name"_a, "shape"_a, "order"_a = UffInputOrder::kNCHW, + UffParserDoc::register_input) + .def("register_output", &IUffParser::registerOutput, "name"_a, UffParserDoc::register_output) + .def("parse", &IUffParser::parse, "file"_a, "network"_a, "weights_type"_a = nvinfer1::DataType::kFLOAT, + UffParserDoc::parse, py::keep_alive<3, 1>{}) + .def("parse_buffer", lambdas::uff_parse_buffer, "buffer"_a, "network"_a, + "weights_type"_a = nvinfer1::DataType::kFLOAT, UffParserDoc::parse_buffer, py::keep_alive<3, 1>{}) + .def_property("error_recorder", &IUffParser::getErrorRecorder, + py::cpp_function(&IUffParser::setErrorRecorder, py::keep_alive<1, 2>{})) + .def("__del__", &utils::doNothingDel); +} +} // namespace tensorrt diff --git a/python/src/pyTensorRT.cpp b/python/src/pyTensorRT.cpp index 069cfd8f..8a49db13 100644 --- a/python/src/pyTensorRT.cpp +++ b/python/src/pyTensorRT.cpp @@ -22,33 +22,33 @@ namespace tensorrt { - PYBIND11_MODULE(tensorrt, m) - { - // Python strings can be automatically converted to FallbackStrings, - // whose lifetime is tied to TRT objects that reference, but do not own, a string. - // See ForwardDeclarations.h for more information about FallbackString. - // Note that we cannot allow Python to deallocate this string, hence the py::nodelete. - py::class_>(m, "FallbackString") - .def(py::init()) - .def(py::init()) - ; - py::implicitly_convertible(); - py::implicitly_convertible(); +PYBIND11_MODULE(tensorrt, m) +{ + // Python strings can be automatically converted to FallbackStrings, + // whose lifetime is tied to TRT objects that reference, but do not own, a string. + // See ForwardDeclarations.h for more information about FallbackString. + // Note that we cannot allow Python to deallocate this string, hence the py::nodelete. + py::class_>(m, "FallbackString") + .def(py::init()) + .def(py::init()); + py::implicitly_convertible(); + py::implicitly_convertible(); - // Make it so that we can use lists of PluginFields without creating unwanted copies. - // This is declared opaque in ForwardDeclarations.h - py::bind_vector>(m, "PluginFieldCollection"); + // Make it so that we can use lists of PluginFields without creating unwanted copies. + // This is declared opaque in ForwardDeclarations.h + py::bind_vector>(m, "PluginFieldCollection"); - // Order matters here - Dependencies must be resolved properly! - bindFoundationalTypes(m); - bindPlugin(m); - bindInt8(m); - bindGraph(m); - bindAlgorithm(m); - bindCore(m); - // Parsers - bindOnnx(m); - bindUff(m); - bindCaffe(m); - } -} /* tensorrt */ + // Order matters here - Dependencies must be resolved properly! + // TODO: Maybe use actual forward declarations and define functions later. + bindFoundationalTypes(m); + bindPlugin(m); + bindInt8(m); + bindGraph(m); + bindAlgorithm(m); + bindCore(m); + // Parsers + bindOnnx(m); + bindUff(m); + bindCaffe(m); +} +} // namespace tensorrt diff --git a/plugin/common/pluginLogger.h b/python/src/utils.cpp similarity index 60% rename from plugin/common/pluginLogger.h rename to python/src/utils.cpp index 5579203a..00eec549 100644 --- a/plugin/common/pluginLogger.h +++ b/python/src/utils.cpp @@ -14,19 +14,20 @@ * limitations under the License. */ -#ifndef PLUGIN_LOGGER_H -#define PLUGIN_LOGGER_H +#include "utils.h" -#include "pluginLogging.h" - -namespace +namespace tensorrt +{ +namespace utils { -Logger gLogger{Logger::Severity::kINFO}; -LogStreamConsumer gLogVerbose{LOG_VERBOSE(gLogger)}; -LogStreamConsumer gLogInfo{LOG_INFO(gLogger)}; -LogStreamConsumer gLogWarning{LOG_WARN(gLogger)}; -LogStreamConsumer gLogError{LOG_ERROR(gLogger)}; -LogStreamConsumer gLogFatal{LOG_FATAL(gLogger)}; -} // namespace -#endif // PLUGIN_LOGGER_H +void issueDeprecationWarning(const char* useInstead) +{ + std::string msg{"Use " + std::string{useInstead} + " instead."}; + + py::gil_scoped_acquire acquire{}; + PyErr_WarnEx(PyExc_DeprecationWarning, msg.c_str(), 1); +} + +} // namespace utils +} // namespace tensorrt diff --git a/requirements.txt b/requirements.txt index 3c6b800c..83f4709f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,8 @@ -onnx==1.7.0 -onnxruntime==1.6.0 -tensorflow-gpu>=2.2 -Pillow>=8.1.2 -pycuda +onnx==1.8.1 +onnxruntime==1.7.0 +tensorflow-gpu==2.4.1 +torch==1.8.1 +Pillow==8.1.2 numpy +pycuda<2020.1 pytest diff --git a/samples/README.md b/samples/README.md index 62e775af..a3e0ca25 100644 --- a/samples/README.md +++ b/samples/README.md @@ -14,12 +14,9 @@ | [sampleMLP](opensource/sampleMLP) | C++ | INetwork | “Hello World” For Multilayer Perceptron (MLP) | | [sampleMNIST](opensource/sampleMNIST) | C++ | Caffe | “Hello World” For TensorRT | | [sampleMNISTAPI](opensource/sampleMNISTAPI) | C++ | INetwork | Building a Simple MNIST Network Layer by Layer | -| [sampleMovieLens](opensource/sampleMovieLens) | C++ | UFF | Movie Recommendation Using Neural Collaborative Filter | -| [sampleMovieLensMPS](opensource/sampleMovieLensMPS) | C++ | UFF | Movie Recommendation With MPS (Multi-Process Service) | | [sampleNMT](opensource/sampleNMT) | C++ | INetwork | Neural Machine Translation Using A seq2seq Model | | [sampleOnnxMNIST](opensource/sampleOnnxMNIST) | C++ | ONNX | “Hello World” For TensorRT With ONNX | | [sampleOnnxMnistCoordConvAC](opensource/sampleOnnxMnistCoordConvAC) | C++ | ONNX | Implementing CoordConv with a custom plugin | -| [samplePlugin](opensource/samplePlugin) | C++ | Caffe | Adding A Custom Layer In TensorRT | | [sampleReformatFreeIO](opensource/sampleReformatFreeIO) | C++ | Caffe | Specifying I/O Formats Via Reformat-Free-I/O API | | [sampleSSD](opensource/sampleSSD) | C++ | Caffe | Object Detection With SSD | | [sampleUffFasterRCNN](opensource/sampleUffFasterRCNN) | C++ | UFF | Object Detection With A TensorFlow FasterRCNN Network | @@ -28,6 +25,8 @@ | [sampleUffPluginV2Ext](opensource/sampleUffPluginV2Ext) | C++ | UFF | Adding A Custom Layer That Supports INT8 I/O To Your Network | | [sampleUffSSD](opensource/sampleUffSSD) | C++ | UFF | Object Detection With A TensorFlow SSD Network | | [trtexec](opensource/trtexec) | C++ | All | TensorRT Command-Line Wrapper: trtexec | +| [efficientdet](python/efficientdet) | Python | ONNX | EfficientDet Object Detection with TensorRT | +| [efficientnet](python/efficientnet) | Python | ONNX | EfficientNet V1 and V2 Classification with TensorRT | | [end_to_end_tensorflow_mnist](python/end_to_end_tensorflow_mnist) | Python | UFF | “Hello World” For TensorRT Using TensorFlow | | [engine_refit_mnist](python/engine_refit_mnist) | Python | INetwork | Refitting A TensorRT Engine | | [int8_caffe_mnist](python/int8_caffe_mnist) | Python | Caffe | INT8 Calibration | diff --git a/samples/common/BatchStream.h b/samples/common/BatchStream.h index b672df4b..b58d0a90 100644 --- a/samples/common/BatchStream.h +++ b/samples/common/BatchStream.h @@ -13,14 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef BATCH_STREAM_H #define BATCH_STREAM_H #include "NvInfer.h" #include "common.h" #include -#include #include #include @@ -92,7 +90,7 @@ public: nvinfer1::Dims getDims() const override { - return Dims{4, {mBatchSize, mDims.d[0], mDims.d[1], mDims.d[2]}, {}}; + return Dims{4, {mBatchSize, mDims.d[0], mDims.d[1], mDims.d[2]}}; } private: @@ -104,7 +102,7 @@ private: file.read(reinterpret_cast(&magicNumber), sizeof(magicNumber)); // All values in the MNIST files are big endian. magicNumber = samplesCommon::swapEndianness(magicNumber); - assert(magicNumber == 2051 && "Magic Number does not match the expected value for an MNIST image set"); + ASSERT(magicNumber == 2051 && "Magic Number does not match the expected value for an MNIST image set"); // Read number of images and dimensions file.read(reinterpret_cast(&numImages), sizeof(numImages)); @@ -131,7 +129,7 @@ private: file.read(reinterpret_cast(&magicNumber), sizeof(magicNumber)); // All values in the MNIST files are big endian. magicNumber = samplesCommon::swapEndianness(magicNumber); - assert(magicNumber == 2049 && "Magic Number does not match the expected value for an MNIST labels file"); + ASSERT(magicNumber == 2049 && "Magic Number does not match the expected value for an MNIST labels file"); file.read(reinterpret_cast(&numImages), sizeof(numImages)); numImages = samplesCommon::swapEndianness(numImages); @@ -163,16 +161,16 @@ public: , mDataDir(directories) { FILE* file = fopen(locateFile(mPrefix + std::string("0") + mSuffix, mDataDir).c_str(), "rb"); - assert(file != nullptr); + ASSERT(file != nullptr); int d[4]; size_t readSize = fread(d, sizeof(int), 4, file); - assert(readSize == 4); + ASSERT(readSize == 4); mDims.nbDims = 4; // The number of dimensions. mDims.d[0] = d[0]; // Batch Size mDims.d[1] = d[1]; // Channels mDims.d[2] = d[2]; // Height mDims.d[3] = d[3]; // Width - assert(mDims.d[0] > 0 && mDims.d[1] > 0 && mDims.d[2] > 0 && mDims.d[3] > 0); + ASSERT(mDims.d[0] > 0 && mDims.d[1] > 0 && mDims.d[2] > 0 && mDims.d[3] > 0); fclose(file); mImageSize = mDims.d[1] * mDims.d[2] * mDims.d[3]; @@ -223,7 +221,7 @@ public: for (int csize = 1, batchPos = 0; batchPos < mBatchSize; batchPos += csize, mFileBatchPos += csize) { - assert(mFileBatchPos > 0 && mFileBatchPos <= mDims.d[0]); + ASSERT(mFileBatchPos > 0 && mFileBatchPos <= mDims.d[0]); if (mFileBatchPos == mDims.d[0] && !update()) { return false; @@ -305,12 +303,12 @@ private: int d[4]; size_t readSize = fread(d, sizeof(int), 4, file); - assert(readSize == 4); - assert(mDims.d[0] == d[0] && mDims.d[1] == d[1] && mDims.d[2] == d[2] && mDims.d[3] == d[3]); + ASSERT(readSize == 4); + ASSERT(mDims.d[0] == d[0] && mDims.d[1] == d[1] && mDims.d[2] == d[2] && mDims.d[3] == d[3]); size_t readInputCount = fread(getFileBatch(), sizeof(float), mDims.d[0] * mImageSize, file); - assert(readInputCount == size_t(mDims.d[0] * mImageSize)); + ASSERT(readInputCount == size_t(mDims.d[0] * mImageSize)); size_t readLabelCount = fread(getFileLabels(), sizeof(float), mDims.d[0], file); - assert(readLabelCount == 0 || readLabelCount == size_t(mDims.d[0])); + ASSERT(readLabelCount == 0 || readLabelCount == size_t(mDims.d[0])); fclose(file); } diff --git a/samples/common/EntropyCalibrator.h b/samples/common/EntropyCalibrator.h index db51c325..f7a01d29 100644 --- a/samples/common/EntropyCalibrator.h +++ b/samples/common/EntropyCalibrator.h @@ -46,24 +46,24 @@ public: CHECK(cudaFree(mDeviceInput)); } - int getBatchSize() const + int getBatchSize() const noexcept { return mStream.getBatchSize(); } - bool getBatch(void* bindings[], const char* names[], int nbBindings) + bool getBatch(void* bindings[], const char* names[], int nbBindings) noexcept { if (!mStream.next()) { return false; } CHECK(cudaMemcpy(mDeviceInput, mStream.getBatch(), mInputCount * sizeof(float), cudaMemcpyHostToDevice)); - assert(!strcmp(names[0], mInputBlobName)); + ASSERT(!strcmp(names[0], mInputBlobName)); bindings[0] = mDeviceInput; return true; } - const void* readCalibrationCache(size_t& length) + const void* readCalibrationCache(size_t& length) noexcept { mCalibrationCache.clear(); std::ifstream input(mCalibrationTableName, std::ios::binary); @@ -77,7 +77,7 @@ public: return length ? mCalibrationCache.data() : nullptr; } - void writeCalibrationCache(const void* cache, size_t length) + void writeCalibrationCache(const void* cache, size_t length) noexcept { std::ofstream output(mCalibrationTableName, std::ios::binary); output.write(reinterpret_cast(cache), length); @@ -108,22 +108,22 @@ public: { } - int getBatchSize() const override + int getBatchSize() const noexcept override { return mImpl.getBatchSize(); } - bool getBatch(void* bindings[], const char* names[], int nbBindings) override + bool getBatch(void* bindings[], const char* names[], int nbBindings) noexcept override { return mImpl.getBatch(bindings, names, nbBindings); } - const void* readCalibrationCache(size_t& length) override + const void* readCalibrationCache(size_t& length) noexcept override { return mImpl.readCalibrationCache(length); } - void writeCalibrationCache(const void* cache, size_t length) override + void writeCalibrationCache(const void* cache, size_t length) noexcept override { mImpl.writeCalibrationCache(cache, length); } diff --git a/samples/common/ErrorRecorder.h b/samples/common/ErrorRecorder.h index abe5537e..b4bc166c 100644 --- a/samples/common/ErrorRecorder.h +++ b/samples/common/ErrorRecorder.h @@ -16,15 +16,19 @@ #ifndef ERROR_RECORDER_H #define ERROR_RECORDER_H -#include "NvInferRuntimeCommon.h" +#include "NvInferRuntime.h" +#include "logger.h" #include #include #include #include #include +#if NV_IS_SAFETY +#include +#endif using namespace nvinfer1; //! -//! A simple imeplementation of the IErrorRecorder interface for +//! A simple implementation of the IErrorRecorder interface for //! use by samples. This interface also can be used as a reference //! implementation. //! The sample Error recorder is based on a vector that pairs the error @@ -49,11 +53,11 @@ public: } ErrorCode getErrorCode(int32_t errorIdx) const noexcept final { - return indexCheck(errorIdx) ? ErrorCode::kINVALID_ARGUMENT : (*this)[errorIdx].first; + return invalidIndexCheck(errorIdx) ? ErrorCode::kINVALID_ARGUMENT : (*this)[errorIdx].first; }; IErrorRecorder::ErrorDesc getErrorDesc(int32_t errorIdx) const noexcept final { - return indexCheck(errorIdx) ? "errorIdx out of range." : (*this)[errorIdx].second.c_str(); + return invalidIndexCheck(errorIdx) ? "errorIdx out of range." : (*this)[errorIdx].second.c_str(); } // This class can never overflow since we have dynamic resize via std::vector usage. bool hasOverflowed() const noexcept final @@ -72,7 +76,11 @@ public: } catch (const std::exception& e) { +#if NV_IS_SAFETY + std::cerr << "Internal Error: " << e.what() << std::endl; +#else getLogger()->log(ILogger::Severity::kINTERNAL_ERROR, e.what()); +#endif } }; @@ -87,11 +95,16 @@ public: try { std::lock_guard guard(mStackLock); + sample::gLogError << "Error[" << static_cast(val) << "]: " << desc << std::endl; mErrorStack.push_back(errorPair(val, desc)); } catch (const std::exception& e) { +#if NV_IS_SAFETY + std::cerr << "Internal Error: " << e.what() << std::endl; +#else getLogger()->log(ILogger::Severity::kINTERNAL_ERROR, e.what()); +#endif } // All errors are considered fatal. return true; @@ -114,7 +127,7 @@ private: return mErrorStack[index]; } - bool indexCheck(int32_t index) const noexcept + bool invalidIndexCheck(int32_t index) const noexcept { // By converting signed to unsigned, we only need a single check since // negative numbers turn into large positive greater than the size. diff --git a/samples/common/argsParser.h b/samples/common/argsParser.h index 7d2604f4..ff0c64ed 100644 --- a/samples/common/argsParser.h +++ b/samples/common/argsParser.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TENSORRT_ARGS_PARSER_H #define TENSORRT_ARGS_PARSER_H diff --git a/samples/common/buffers.h b/samples/common/buffers.h index 98365ca6..60e310b5 100644 --- a/samples/common/buffers.h +++ b/samples/common/buffers.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef TENSORRT_BUFFERS_H #define TENSORRT_BUFFERS_H @@ -455,7 +454,7 @@ private: std::shared_ptr mEngine; //!< The pointer to the engine int mBatchSize; //!< The batch size for legacy networks, 0 otherwise. std::vector> mManagedBuffers; //!< The vector of pointers to managed buffers - std::vector mDeviceBindings; //!< The vector of device buffers needed for engine execution + std::vector mDeviceBindings; //!< The vector of device buffers needed for engine execution }; } // namespace samplesCommon diff --git a/samples/common/common.h b/samples/common/common.h index 91380d97..744482ab 100644 --- a/samples/common/common.h +++ b/samples/common/common.h @@ -59,7 +59,7 @@ using namespace plugin; #define FN_NAME __func__ #endif -#if (!defined(__ANDROID__) && defined(__aarch64__)) || defined(__QNX__) +#if defined(__aarch64__) || defined(__QNX__) #define ENABLE_DLA_API 1 #endif @@ -79,22 +79,24 @@ using namespace plugin; { \ if (!(status)) \ { \ - sample::gLogError << errMsg << " Error in " << __FILE__ << ", function " << FN_NAME << "(), line " \ - << __LINE__ << std::endl; \ + sample::gLogError << errMsg << " Error in " << __FILE__ << ", function " << FN_NAME << "(), line " << __LINE__ \ + << std::endl; \ return val; \ } \ } while (0) -#define ASSERT(condition) \ - do \ - { \ - if (!(condition)) \ - { \ - sample::gLogError << "Assertion failure: " << #condition << std::endl; \ - abort(); \ - } \ +#undef ASSERT +#define ASSERT(condition) \ + do \ + { \ + if (!(condition)) \ + { \ + sample::gLogError << "Assertion failure: " << #condition << std::endl; \ + abort(); \ + } \ } while (0) + #define CHECK_RETURN(status, val) CHECK_RETURN_W_MSG(status, val, "") #define OBJ_GUARD(A) std::unique_ptr @@ -123,15 +125,15 @@ constexpr long double operator"" _KiB(long double val) // These is necessary if we want to be able to write 1_GiB instead of 1.0_GiB. // Since the return type is signed, -1_GiB will work as expected. -constexpr long long int operator"" _GiB(long long unsigned int val) +constexpr long long int operator"" _GiB(unsigned long long val) { return val * (1 << 30); } -constexpr long long int operator"" _MiB(long long unsigned int val) +constexpr long long int operator"" _MiB(unsigned long long val) { return val * (1 << 20); } -constexpr long long int operator"" _KiB(long long unsigned int val) +constexpr long long int operator"" _KiB(unsigned long long val) { return val * (1 << 10); } @@ -144,7 +146,7 @@ struct SimpleProfiler : public nvinfer1::IProfiler int count{0}; }; - virtual void reportLayerTime(const char* layerName, float ms) + virtual void reportLayerTime(const char* layerName, float ms) noexcept { mProfile[layerName].count++; mProfile[layerName].time += ms; @@ -224,7 +226,8 @@ private: //! Locate path to file, given its filename or filepath suffix and possible dirs it might lie in. //! Function will also walk back MAX_DEPTH dirs from CWD to check for such a file path. -inline std::string locateFile(const std::string& filepathSuffix, const std::vector& directories) +inline std::string locateFile( + const std::string& filepathSuffix, const std::vector& directories, bool reportError = true) { const int MAX_DEPTH{10}; bool found{false}; @@ -271,8 +274,12 @@ inline std::string locateFile(const std::string& filepathSuffix, const std::vect const std::string dirList = std::accumulate(directories.begin() + 1, directories.end(), directories.front(), [](const std::string& a, const std::string& b) { return a + "\n\t" + b; }); std::cout << "Could not find " << filepathSuffix << " in data directories:\n\t" << dirList << std::endl; - std::cout << "&&&& FAILED" << std::endl; - exit(EXIT_FAILURE); + + if (reportError) + { + std::cout << "&&&& FAILED" << std::endl; + exit(EXIT_FAILURE); + } } return filepath; @@ -303,22 +310,23 @@ inline T swapEndianness(const T& value) return *reinterpret_cast(bytes); } -class HostMemory : public IHostMemory +class HostMemory { public: HostMemory() = delete; - void* data() const noexcept override + virtual void* data() const noexcept { return mData; } - std::size_t size() const noexcept override + virtual std::size_t size() const noexcept { return mSize; } - DataType type() const noexcept override + virtual DataType type() const noexcept { return mType; } + virtual ~HostMemory() {} protected: HostMemory(std::size_t size, DataType type) @@ -340,10 +348,9 @@ public: { mData = new ElemType[size]; }; - void destroy() noexcept override + ~TypedHostMemory() noexcept { delete[](ElemType*) mData; - delete this; } ElemType* raw() noexcept { @@ -377,21 +384,41 @@ struct InferDeleter template void operator()(T* obj) const { - if (obj) - { - obj->destroy(); - } + delete obj; } }; +template +using SampleUniquePtr = std::unique_ptr; + +static auto StreamDeleter = [](cudaStream_t* pStream) + { + if (pStream) + { + cudaStreamDestroy(*pStream); + delete pStream; + } + }; + +inline std::unique_ptr makeCudaStream() +{ + std::unique_ptr pStream(new cudaStream_t, StreamDeleter); + if (cudaStreamCreate(pStream.get()) != cudaSuccess) + { + pStream.reset(nullptr); + } + + return pStream; +} + template std::shared_ptr infer_object(T* obj) { if (!obj) { - throw std::runtime_error("Failed to create object"); + throw std::runtime_error(std::string("Failed to create object")); } - return std::shared_ptr(obj, InferDeleter()); + return std::shared_ptr(obj); } //! Return vector of indices that puts magnitudes of sequence in descending order. @@ -400,8 +427,7 @@ std::vector argMagnitudeSort(Iter begin, Iter end) { std::vector indices(end - begin); std::iota(indices.begin(), indices.end(), 0); - std::sort(indices.begin(), indices.end(), - [&begin](size_t i, size_t j) { return std::abs(begin[j]) < std::abs(begin[i]); }); + std::sort(indices.begin(), indices.end(), [&begin](size_t i, size_t j) { return std::abs(begin[j]) < std::abs(begin[i]); }); return indices; } @@ -529,7 +555,7 @@ inline void setAllTensorScales(INetworkDefinition* network, float inScales = 2.0 // Optional inputs are nullptr here and are from RNN layers. if (input != nullptr && !input->dynamicRangeIsSet()) { - ASSERT(input->setDynamicRange(-inScales, inScales)); + input->setDynamicRange(-inScales, inScales); } } } @@ -549,26 +575,31 @@ inline void setAllTensorScales(INetworkDefinition* network, float inScales = 2.0 // Pooling must have the same input and output scales. if (layer->getType() == LayerType::kPOOLING) { - ASSERT(output->setDynamicRange(-inScales, inScales)); + output->setDynamicRange(-inScales, inScales); } else { - ASSERT(output->setDynamicRange(-outScales, outScales)); + output->setDynamicRange(-outScales, outScales); } } } } } -inline void setDummyInt8Scales(const IBuilderConfig* c, INetworkDefinition* n) +inline void setAllDynamicRanges(INetworkDefinition* network, float inRange = 2.0f, float outRange = 4.0f) { - // Set dummy tensor scales if Int8 mode is requested. + return setAllTensorScales(network, inRange, outRange); +} + +inline void setDummyInt8DynamicRanges(const IBuilderConfig* c, INetworkDefinition* n) +{ + // Set dummy per-tensor dynamic range if Int8 mode is requested. if (c->getFlag(BuilderFlag::kINT8)) { sample::gLogWarning - << "Int8 calibrator not provided. Generating dummy per tensor scales. Int8 accuracy is not guaranteed." + << "Int8 calibrator not provided. Generating dummy per-tensor dynamic range. Int8 accuracy is not guaranteed." << std::endl; - setAllTensorScales(n); + setAllDynamicRanges(n); } } @@ -586,11 +617,10 @@ inline void enableDLA(IBuilder* builder, IBuilderConfig* config, int useDLACore, { config->setFlag(BuilderFlag::kGPU_FALLBACK); } - if (!builder->getInt8Mode() && !config->getFlag(BuilderFlag::kINT8)) + if (!config->getFlag(BuilderFlag::kINT8)) { // User has not requested INT8 Mode. // By default run in FP16 mode. FP32 mode is not permitted. - builder->setFp16Mode(true); config->setFlag(BuilderFlag::kFP16); } config->setDefaultDeviceType(DeviceType::kDLA); @@ -610,7 +640,7 @@ inline int parseDLA(int argc, char** argv) return -1; } -inline unsigned int getElementSize(nvinfer1::DataType t) +inline uint32_t getElementSize(nvinfer1::DataType t) noexcept { switch (t) { @@ -620,7 +650,6 @@ inline unsigned int getElementSize(nvinfer1::DataType t) case nvinfer1::DataType::kBOOL: case nvinfer1::DataType::kINT8: return 1; } - throw std::runtime_error("Invalid DataType."); return 0; } @@ -629,7 +658,7 @@ inline int64_t volume(const nvinfer1::Dims& d) return std::accumulate(d.d, d.d + d.nbDims, 1, std::multiplies()); } -inline unsigned int elementSize(DataType t) +inline uint32_t elementSize(DataType t) noexcept { switch (t) { @@ -910,6 +939,23 @@ inline void loadLibrary(const std::string& path) } } +inline int32_t getSMVersion() +{ + int32_t deviceIndex = 0; + CHECK(cudaGetDevice(&deviceIndex)); + + int32_t major, minor; + CHECK(cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, deviceIndex)); + CHECK(cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, deviceIndex)); + + return ((major << 8) | minor); +} + +inline bool isSMSafe() +{ + const int32_t smVersion = getSMVersion(); + return smVersion == 0x0700 || smVersion == 0x0702 || smVersion == 0x0705; +} } // namespace samplesCommon inline std::ostream& operator<<(std::ostream& os, const nvinfer1::Dims& dims) diff --git a/samples/common/half.h b/samples/common/half.h index 12237135..0755c316 100644 --- a/samples/common/half.h +++ b/samples/common/half.h @@ -888,7 +888,7 @@ uint16 int2half_impl(T value) } else if (value) { - unsigned int m = value, exp = 24; + uint32_t m = value, exp = 24; for (; m < 0x400; m <<= 1, --exp) ; for (; m > 0x7FF; m >>= 1, ++exp) @@ -1250,7 +1250,7 @@ T half2int_impl(uint16 value) #if HALF_ENABLE_CPP11_STATIC_ASSERT && HALF_ENABLE_CPP11_TYPE_TRAITS static_assert(std::is_integral::value, "half to int conversion only supports builtin integer types"); #endif - unsigned int e = value & 0x7FFF; + uint32_t e = value & 0x7FFF; if (e >= 0x7C00) return (value & 0x8000) ? std::numeric_limits::min() : std::numeric_limits::max(); if (e < 0x3800) @@ -1261,7 +1261,7 @@ T half2int_impl(uint16 value) return -T(value > 0x8000); return T(); } - unsigned int m = (value & 0x3FF) | 0x400; + uint32_t m = (value & 0x3FF) | 0x400; e >>= 10; if (e < 25) { @@ -1305,7 +1305,7 @@ T half2int_up(uint16 value) template uint16 round_half_impl(uint16 value) { - unsigned int e = value & 0x7FFF; + uint32_t e = value & 0x7FFF; uint16 result = value; if (e < 0x3C00) { @@ -1320,7 +1320,7 @@ uint16 round_half_impl(uint16 value) else if (e < 0x6400) { e = 25 - (e >> 10); - unsigned int mask = (1 << e) - 1; + uint32_t mask = (1 << e) - 1; if (R == std::round_to_nearest) result += (1 << (e - 1)) - (~(result >> e) & E); else if (R == std::round_toward_infinity) @@ -2166,13 +2166,13 @@ struct functions /// \return fractional part static half modf(half arg, half* iptr) { - unsigned int e = arg.data_ & 0x7FFF; + uint32_t e = arg.data_ & 0x7FFF; if (e >= 0x6400) return *iptr = arg, half(binary, arg.data_ & (0x8000U | -(e > 0x7C00))); if (e < 0x3C00) return iptr->data_ = arg.data_ & 0x8000, arg; e >>= 10; - unsigned int mask = (1 << (25 - e)) - 1, m = arg.data_ & mask; + uint32_t mask = (1 << (25 - e)) - 1, m = arg.data_ & mask; iptr->data_ = arg.data_ & ~mask; if (!m) return half(binary, arg.data_ & 0x8000); @@ -2187,7 +2187,7 @@ struct functions /// \return scaled number static half scalbln(half arg, long exp) { - unsigned int m = arg.data_ & 0x7FFF; + uint32_t m = arg.data_ & 0x7FFF; if (m >= 0x7C00 || !m) return arg; for (; m < 0x400; m <<= 1, --exp) @@ -2268,7 +2268,7 @@ struct functions uint16 bits = (exp < 0) << 15; if (exp) { - unsigned int m = std::abs(exp) << 6, e = 18; + uint32_t m = std::abs(exp) << 6, e = 18; for (; m < 0x400; m <<= 1, --e) ; bits |= (e << 10) + m; @@ -2329,7 +2329,7 @@ struct functions /// \retval false else static int fpclassify(half arg) { - unsigned int abs = arg.data_ & 0x7FFF; + uint32_t abs = arg.data_ & 0x7FFF; return abs ? ((abs > 0x3FF) ? ((abs >= 0x7C00) ? ((abs > 0x7C00) ? FP_NAN : FP_INFINITE) : FP_NORMAL) : FP_SUBNORMAL) : FP_ZERO; diff --git a/samples/common/logger.cpp b/samples/common/logger.cpp index 4c503d51..ffb65c64 100644 --- a/samples/common/logger.cpp +++ b/samples/common/logger.cpp @@ -15,8 +15,10 @@ */ #include "logger.h" +#include "ErrorRecorder.h" #include "logging.h" +SampleErrorRecorder gRecorder; namespace sample { Logger gLogger{Logger::Severity::kINFO}; diff --git a/samples/common/logger.h b/samples/common/logger.h index aa7a7921..af9bb254 100644 --- a/samples/common/logger.h +++ b/samples/common/logger.h @@ -19,6 +19,8 @@ #include "logging.h" +class SampleErrorRecorder; +extern SampleErrorRecorder gRecorder; namespace sample { extern Logger gLogger; diff --git a/samples/common/logging.h b/samples/common/logging.h index c6cd2094..3f5685cb 100644 --- a/samples/common/logging.h +++ b/samples/common/logging.h @@ -17,7 +17,7 @@ #ifndef TENSORRT_LOGGING_H #define TENSORRT_LOGGING_H -#include "NvInferRuntimeCommon.h" +#include "NvInferRuntime.h" #include #include #include @@ -230,7 +230,7 @@ public: //! TODO Once all samples are updated to use this method to register the logger with TensorRT, //! we can eliminate the inheritance of Logger from ILogger //! - nvinfer1::ILogger& getTRTLogger() + nvinfer1::ILogger& getTRTLogger() noexcept { return *this; } @@ -241,7 +241,7 @@ public: //! Note samples should not be calling this function directly; it will eventually go away once we eliminate the //! inheritance from nvinfer1::ILogger //! - void log(Severity severity, const char* msg) override + void log(Severity severity, const char* msg) noexcept override { LogStreamConsumer(mReportableSeverity, severity) << "[TRT] " << std::string(msg) << std::endl; } @@ -310,8 +310,10 @@ public: //! \return a TestAtom that can be used in Logger::reportTest{Start,End}(). static TestAtom defineTest(const std::string& name, int argc, char const* const* argv) { + // Append TensorRT version as info + const std::string vname = name + " [TensorRT v" + std::to_string(NV_TENSORRT_VERSION) + "]"; auto cmdline = genCmdlineString(argc, argv); - return defineTest(name, cmdline); + return defineTest(vname, cmdline); } //! diff --git a/samples/common/parserOnnxConfig.h b/samples/common/parserOnnxConfig.h index 525099a2..4ffe85d0 100644 --- a/samples/common/parserOnnxConfig.h +++ b/samples/common/parserOnnxConfig.h @@ -74,67 +74,67 @@ protected: } public: - virtual void setModelDtype(const nvinfer1::DataType modelDtype) + virtual void setModelDtype(const nvinfer1::DataType modelDtype) noexcept { mModelDtype = modelDtype; } - virtual nvinfer1::DataType getModelDtype() const + virtual nvinfer1::DataType getModelDtype() const noexcept { return mModelDtype; } - virtual const char* getModelFileName() const + virtual const char* getModelFileName() const noexcept { return mModelFilename.c_str(); } - virtual void setModelFileName(const char* onnxFilename) + virtual void setModelFileName(const char* onnxFilename) noexcept { mModelFilename = string(onnxFilename); } - virtual nvonnxparser::IOnnxConfig::Verbosity getVerbosityLevel() const + virtual nvonnxparser::IOnnxConfig::Verbosity getVerbosityLevel() const noexcept { return mVerbosity; } - virtual void addVerbosity() + virtual void addVerbosity() noexcept { ++mVerbosity; } - virtual void reduceVerbosity() + virtual void reduceVerbosity() noexcept { --mVerbosity; } - virtual void setVerbosityLevel(nvonnxparser::IOnnxConfig::Verbosity verbosity) + virtual void setVerbosityLevel(nvonnxparser::IOnnxConfig::Verbosity verbosity) noexcept { mVerbosity = verbosity; } - virtual const char* getTextFileName() const + virtual const char* getTextFileName() const noexcept { return mTextFilename.c_str(); } - virtual void setTextFileName(const char* textFilename) + virtual void setTextFileName(const char* textFilename) noexcept { mTextFilename = string(textFilename); } - virtual const char* getFullTextFileName() const + virtual const char* getFullTextFileName() const noexcept { return mFullTextFilename.c_str(); } - virtual void setFullTextFileName(const char* fullTextFilename) + virtual void setFullTextFileName(const char* fullTextFilename) noexcept { mFullTextFilename = string(fullTextFilename); } - virtual bool getPrintLayerInfo() const + virtual bool getPrintLayerInfo() const noexcept { return mPrintLayercInfo; } - virtual void setPrintLayerInfo(bool src) + virtual void setPrintLayerInfo(bool src) noexcept { mPrintLayercInfo = src; } //!< get the boolean variable corresponding to the Layer Info, see getPrintLayerInfo() - virtual bool isDebug() const + virtual bool isDebug() const noexcept { #if ONNX_DEBUG return (std::getenv("ONNX_DEBUG") ? true : false); @@ -143,7 +143,7 @@ public: #endif } - virtual void destroy() + virtual void destroy() noexcept { delete this; } diff --git a/samples/common/safeCommon.h b/samples/common/safeCommon.h new file mode 100644 index 00000000..35310c80 --- /dev/null +++ b/samples/common/safeCommon.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ + +#ifndef TENSORRT_SAFE_COMMON_H +#define TENSORRT_SAFE_COMMON_H + +#include +#include +#include +#include +#include + +#define CHECK(status) \ + do \ + { \ + auto ret = (status); \ + if (ret != 0) \ + { \ + std::cerr << "Cuda failure: " << ret << std::endl; \ + abort(); \ + } \ + } while (0) + +namespace samplesCommon +{ +template +inline std::shared_ptr infer_object(T* obj) +{ + if (!obj) + { + throw std::runtime_error("Failed to create object"); + } + return std::shared_ptr(obj); +} + +inline uint32_t elementSize(DataType t) +{ + switch (t) + { + case DataType::kINT32: + case DataType::kFLOAT: return 4; + case DataType::kHALF: return 2; + case DataType::kINT8: return 1; + case DataType::kBOOL: return 1; + } + return 0; +} + +template +inline A divUp(A x, B n) +{ + return (x + n - 1) / n; +} + +} // namespace samplesCommon + +#endif // TENSORRT_SAFE_COMMON_H diff --git a/samples/common/sampleConfig.h b/samples/common/sampleConfig.h index ef2f4074..7445a895 100644 --- a/samples/common/sampleConfig.h +++ b/samples/common/sampleConfig.h @@ -41,6 +41,7 @@ private: std::string mReferenceFilename; std::string mOutputFilename; std::string mCalibrationFilename; + std::string mTimingCacheFilename; int64_t mLabel{-1}; int64_t mMaxBatchSize{32}; int64_t mMaxWorkspaceSize{1 * 1024 * 1024 * 1024}; @@ -57,6 +58,7 @@ private: uint64_t mTopK{0}; float mFailurePercentage{-1.0f}; float mTolerance{0.0f}; + float mAbsTolerance{1e-5f}; public: SampleConfig() @@ -81,229 +83,252 @@ protected: } public: - void setModelDtype(const nvinfer1::DataType mdt) + void setModelDtype(const nvinfer1::DataType mdt) noexcept { mModelDtype = mdt; } - nvinfer1::DataType getModelDtype() const + nvinfer1::DataType getModelDtype() const noexcept { return mModelDtype; } - bool getTF32() const + bool getTF32() const noexcept { return mTF32; } - void setTF32(bool enabled) + void setTF32(bool enabled) noexcept { mTF32 = enabled; } - const char* getModelFileName() const + const char* getModelFileName() const noexcept { return mModelFilename.c_str(); } - void setModelFileName(const char* onnxFilename) + void setModelFileName(const char* onnxFilename) noexcept { - mModelFilename = string(onnxFilename); + mModelFilename = std::string(onnxFilename); } - Verbosity getVerbosityLevel() const + Verbosity getVerbosityLevel() const noexcept { return mVerbosity; } - void addVerbosity() + void addVerbosity() noexcept { ++mVerbosity; } - void reduceVerbosity() + void reduceVerbosity() noexcept { --mVerbosity; } - virtual void setVerbosityLevel(Verbosity v) + virtual void setVerbosityLevel(Verbosity v) noexcept { mVerbosity = v; } - const char* getEngineFileName() const + const char* getEngineFileName() const noexcept { return mEngineFilename.c_str(); } - void setEngineFileName(const char* engineFilename) + void setEngineFileName(const char* engineFilename) noexcept { - mEngineFilename = string(engineFilename); + mEngineFilename = std::string(engineFilename); } - const char* getTextFileName() const + const char* getTextFileName() const noexcept { return mTextFilename.c_str(); } - void setTextFileName(const char* textFilename) + void setTextFileName(const char* textFilename) noexcept { - mTextFilename = string(textFilename); + mTextFilename = std::string(textFilename); } - const char* getFullTextFileName() const + const char* getFullTextFileName() const noexcept { return mFullTextFilename.c_str(); } - void setFullTextFileName(const char* fullTextFilename) + void setFullTextFileName(const char* fullTextFilename) noexcept { - mFullTextFilename = string(fullTextFilename); + mFullTextFilename = std::string(fullTextFilename); } - void setLabel(int64_t label) + void setLabel(int64_t label) noexcept { mLabel = label; } //!< set the Label - int64_t getLabel() const + + int64_t getLabel() const noexcept { return mLabel; } //!< get the Label - bool getPrintLayerInfo() const + + bool getPrintLayerInfo() const noexcept { return mPrintLayercInfo; } - void setPrintLayerInfo(bool b) + + void setPrintLayerInfo(bool b) noexcept { mPrintLayercInfo = b; } //!< get the boolean variable corresponding to the Layer Info, see getPrintLayerInfo() - void setMaxBatchSize(int64_t maxBatchSize) + void setMaxBatchSize(int64_t maxBatchSize) noexcept { mMaxBatchSize = maxBatchSize; } //!< set the Max Batch Size - int64_t getMaxBatchSize() const + int64_t getMaxBatchSize() const noexcept { return mMaxBatchSize; } //!< get the Max Batch Size - void setMaxWorkSpaceSize(int64_t maxWorkSpaceSize) + void setMaxWorkSpaceSize(int64_t maxWorkSpaceSize) noexcept { mMaxWorkspaceSize = maxWorkSpaceSize; } //!< set the Max Work Space size - int64_t getMaxWorkSpaceSize() const + int64_t getMaxWorkSpaceSize() const noexcept { return mMaxWorkspaceSize; } //!< get the Max Work Space size - void setCalibBatchSize(int64_t CalibBatchSize) + void setCalibBatchSize(int64_t CalibBatchSize) noexcept { mCalibBatchSize = CalibBatchSize; } //!< set the calibration batch size - int64_t getCalibBatchSize() const + int64_t getCalibBatchSize() const noexcept { return mCalibBatchSize; } //!< get calibration batch size - void setMaxNCalibBatch(int64_t MaxNCalibBatch) + void setMaxNCalibBatch(int64_t MaxNCalibBatch) noexcept { mMaxNCalibBatch = MaxNCalibBatch; } //!< set Max Number of Calibration Batches - int64_t getMaxNCalibBatch() const + int64_t getMaxNCalibBatch() const noexcept { return mMaxNCalibBatch; } //!< get the Max Number of Calibration Batches - void setFirstCalibBatch(int64_t FirstCalibBatch) + void setFirstCalibBatch(int64_t FirstCalibBatch) noexcept { mFirstCalibBatch = FirstCalibBatch; } //!< set the first calibration batch - int64_t getFirstCalibBatch() const + int64_t getFirstCalibBatch() const noexcept { return mFirstCalibBatch; } //!< get the first calibration batch - void setUseDLACore(int64_t UseDLACore) + void setUseDLACore(int64_t UseDLACore) noexcept { mUseDLACore = UseDLACore; } //!< set the DLA core to use - int64_t getUseDLACore() const + int64_t getUseDLACore() const noexcept { return mUseDLACore; } //!< get the DLA core to use - void setDebugBuilder() + void setDebugBuilder() noexcept { mDebugBuilder = true; } //!< enable the Debug info, while building the engine. - bool getDebugBuilder() const + bool getDebugBuilder() const noexcept { return mDebugBuilder; } //!< get the boolean variable, corresponding to the debug builder - const char* getImageFileName() const //!< set Image file name (PPM or ASCII) + const char* getImageFileName() const noexcept //!< set Image file name (PPM or ASCII) { return mImageFilename.c_str(); } - void setImageFileName(const char* imageFilename) //!< get the Image file name + void setImageFileName(const char* imageFilename) noexcept //!< get the Image file name { - mImageFilename = string(imageFilename); + mImageFilename = std::string(imageFilename); } - const char* getReferenceFileName() const + const char* getReferenceFileName() const noexcept { return mReferenceFilename.c_str(); } - void setReferenceFileName(const char* referenceFilename) //!< set reference file name + void setReferenceFileName(const char* referenceFilename) noexcept //!< set reference file name { - mReferenceFilename = string(referenceFilename); + mReferenceFilename = std::string(referenceFilename); } - void setInputDataFormat(InputDataFormat idt) + void setInputDataFormat(InputDataFormat idt) noexcept { mInputDataFormat = idt; } //!< specifies expected data format of the image file (PPM or ASCII) - InputDataFormat getInputDataFormat() const + InputDataFormat getInputDataFormat() const noexcept { return mInputDataFormat; } //!< returns the expected data format of the image file. - const char* getOutputFileName() const //!< specifies the file to save the results + const char* getOutputFileName() const noexcept //!< specifies the file to save the results { return mOutputFilename.c_str(); } - void setOutputFileName(const char* outputFilename) //!< get the output file name + void setOutputFileName(const char* outputFilename) noexcept //!< get the output file name { - mOutputFilename = string(outputFilename); + mOutputFilename = std::string(outputFilename); } - const char* getCalibrationFileName() const + const char* getCalibrationFileName() const noexcept { return mCalibrationFilename.c_str(); } //!< specifies the file containing the list of image files for int8 calibration - void setCalibrationFileName(const char* calibrationFilename) //!< get the int 8 calibration list file name + void setCalibrationFileName(const char* calibrationFilename) noexcept //!< get the int 8 calibration list file name { - mCalibrationFilename = string(calibrationFilename); + mCalibrationFilename = std::string(calibrationFilename); } - uint64_t getTopK() const + uint64_t getTopK() const noexcept { return mTopK; } - void setTopK(uint64_t topK) + void setTopK(uint64_t topK) noexcept { mTopK = topK; } //!< If this options is specified, return the K top probabilities. - float getFailurePercentage() const + float getFailurePercentage() const noexcept { return mFailurePercentage; } - void setFailurePercentage(float f) + void setFailurePercentage(float f) noexcept { mFailurePercentage = f; } - float getTolerance() const + float getAbsoluteTolerance() const noexcept + { + return mAbsTolerance; + } + + void setAbsoluteTolerance(float a) noexcept + { + mAbsTolerance = a; + } + + float getTolerance() const noexcept { return mTolerance; } - void setTolerance(float t) + void setTolerance(float t) noexcept { mTolerance = t; } - bool isDebug() const + const char* getTimingCacheFilename() const noexcept + { + return mTimingCacheFilename.c_str(); + } + + void setTimingCacheFileName(const char* timingCacheFilename) noexcept + { + mTimingCacheFilename = std::string(timingCacheFilename); + } + + bool isDebug() const noexcept { #if ONNX_DEBUG return (std::getenv("ONNX_DEBUG") ? true : false); @@ -312,7 +337,7 @@ public: #endif } - void destroy() + void destroy() noexcept { delete this; } diff --git a/samples/common/sampleDevice.h b/samples/common/sampleDevice.h index 1ce9a24d..4617292f 100644 --- a/samples/common/sampleDevice.h +++ b/samples/common/sampleDevice.h @@ -17,6 +17,7 @@ #ifndef TRT_SAMPLE_DEVICE_H #define TRT_SAMPLE_DEVICE_H +#include #include #include #include @@ -109,7 +110,7 @@ class TrtCudaEvent public: explicit TrtCudaEvent(bool blocking = true) { - const unsigned int flags = blocking ? cudaEventBlockingSync : cudaEventDefault; + const uint32_t flags = blocking ? cudaEventBlockingSync : cudaEventDefault; cudaCheck(cudaEventCreateWithFlags(&mEvent, flags)); } @@ -189,9 +190,9 @@ public: cudaCheck(cudaStreamBeginCapture(stream.get(), cudaStreamCaptureModeThreadLocal)); } - void launch(TrtCudaStream& stream) + bool launch(TrtCudaStream& stream) { - cudaCheck(cudaGraphLaunch(mGraphExec, stream.get())); + return cudaGraphLaunch(mGraphExec, stream.get()) == cudaSuccess; } void endCapture(TrtCudaStream& stream) @@ -201,6 +202,16 @@ public: cudaCheck(cudaGraphDestroy(mGraph)); } + void endCaptureOnError(TrtCudaStream& stream) + { + const auto ret = cudaStreamEndCapture(stream.get(), &mGraph); + assert(ret == cudaErrorStreamCaptureInvalidated); + assert(mGraph == nullptr); + // Clean up the above CUDA error. + cudaGetLastError(); + sample::gLogWarning << "The CUDA graph capture on the stream has failed." << std::endl; + } + private: cudaGraph_t mGraph{}; cudaGraphExec_t mGraphExec{}; @@ -351,6 +362,7 @@ private: TrtDeviceBuffer mDeviceBuffer; }; + inline void setCudaDevice(int device, std::ostream& os) { cudaCheck(cudaSetDevice(device)); @@ -358,7 +370,7 @@ inline void setCudaDevice(int device, std::ostream& os) cudaDeviceProp properties; cudaCheck(cudaGetDeviceProperties(&properties, device)); - // clang-format off +// clang-format off os << "=== Device Information ===" << std::endl; os << "Selected Device: " << properties.name << std::endl; os << "Compute Capability: " << properties.major << "." << properties.minor << std::endl; diff --git a/samples/common/sampleEngines.cpp b/samples/common/sampleEngines.cpp index 7648e4a2..d07c617c 100644 --- a/samples/common/sampleEngines.cpp +++ b/samples/common/sampleEngines.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "NvCaffeParser.h" @@ -27,6 +28,9 @@ #include "NvOnnxParser.h" #include "NvUffParser.h" +#include "common.h" +#include "ErrorRecorder.h" +#include "half.h" #include "logger.h" #include "sampleEngines.h" #include "sampleOptions.h" @@ -56,19 +60,72 @@ struct UffBufferShutter } }; +std::map readScalesFromCalibrationCache(const std::string& calibrationFile) +{ + std::map tensorScales; + std::ifstream cache{calibrationFile}; + if (!cache.is_open()) + { + sample::gLogError << "[TRT] Can not open provided calibration cache file" << std::endl; + return tensorScales; + } + std::string line; + while (std::getline(cache, line)) + { + auto colonPos = line.find_last_of(':'); + if (colonPos != std::string::npos) + { + // Scales should be stored in calibration cache as 32-bit floating numbers encoded as 32-bit integers + int32_t scalesAsInt = std::stoi(line.substr(colonPos + 2, 8), nullptr, 16); + const auto tensorName = line.substr(0, colonPos); + tensorScales[tensorName] = *reinterpret_cast(&scalesAsInt); + } + } + cache.close(); + return tensorScales; +} } // namespace +void setTensorScalesFromCalibration(nvinfer1::INetworkDefinition& network, const std::vector& inputFormats, + const std::vector& outputFormats, const std::string& calibrationFile) +{ + const auto tensorScales = readScalesFromCalibrationCache(calibrationFile); + const bool broadcastInputFormats = broadcastIOFormats(inputFormats, network.getNbInputs()); + for (int32_t i = 0, n = network.getNbInputs(); i < n; ++i) + { + int32_t formatIdx = broadcastInputFormats ? 0 : i; + if (!inputFormats.empty() && inputFormats[formatIdx].first == DataType::kINT8) + { + auto* input = network.getInput(i); + const auto calibScale = tensorScales.at(input->getName()); + input->setDynamicRange(-127 * calibScale, 127 * calibScale); + } + } + const bool broadcastOutputFormats = broadcastIOFormats(outputFormats, network.getNbInputs()); + for (int32_t i = 0, n = network.getNbOutputs(); i < n; ++i) + { + int32_t formatIdx = broadcastOutputFormats ? 0 : i; + if (!outputFormats.empty() && outputFormats[formatIdx].first == DataType::kINT8) + { + auto* output = network.getOutput(i); + const auto calibScale = tensorScales.at(output->getName()); + output->setDynamicRange(-127 * calibScale, 127 * calibScale); + } + } +} + #define SMP_RETVAL_IF_FALSE(condition, msg, retval, err) \ { \ if ((condition) == false) \ { \ - err << msg << std::endl; \ + (err) << (msg) << std::endl; \ return retval; \ } \ } Parser modelToNetwork(const ModelOptions& model, nvinfer1::INetworkDefinition& network, std::ostream& err) { + sample::gLogInfo << "Start parsing network model" << std::endl; Parser parser; const std::string& modelName = model.baseModel.model; switch (model.baseModel.format) @@ -78,7 +135,7 @@ Parser modelToNetwork(const ModelOptions& model, nvinfer1::INetworkDefinition& n using namespace nvcaffeparser1; parser.caffeParser.reset(createCaffeParser()); CaffeBufferShutter bufferShutter; - const auto blobNameToTensor = parser.caffeParser->parse( + const auto* const blobNameToTensor = parser.caffeParser->parse( model.prototxt.c_str(), modelName.empty() ? nullptr : modelName.c_str(), network, DataType::kFLOAT); if (!blobNameToTensor) { @@ -148,6 +205,7 @@ Parser modelToNetwork(const ModelOptions& model, nvinfer1::INetworkDefinition& n case ModelFormat::kANY: break; } + sample::gLogInfo << "Finish parsing network model" << std::endl; return parser; } @@ -157,7 +215,7 @@ namespace class RndInt8Calibrator : public nvinfer1::IInt8EntropyCalibrator2 { public: - RndInt8Calibrator(int batches, std::vector& elemCount, const std::string& cacheFile, + RndInt8Calibrator(int batches, std::vector& elemCount, const std::string& cacheFile, const nvinfer1::INetworkDefinition& network, std::ostream& err); ~RndInt8Calibrator() @@ -168,16 +226,16 @@ public: } } - bool getBatch(void* bindings[], const char* names[], int nbBindings) override; + bool getBatch(void* bindings[], const char* names[], int nbBindings) noexcept override; - int getBatchSize() const override + int getBatchSize() const noexcept override { return 1; } - const void* readCalibrationCache(size_t& length) override; + const void* readCalibrationCache(size_t& length) noexcept override; - virtual void writeCalibrationCache(const void*, size_t) override {} + virtual void writeCalibrationCache(const void*, size_t) noexcept override {} private: int mBatches{}; @@ -188,7 +246,7 @@ private: std::ostream& mErr; }; -RndInt8Calibrator::RndInt8Calibrator(int batches, std::vector& elemCount, const std::string& cacheFile, +RndInt8Calibrator::RndInt8Calibrator(int batches, std::vector& elemCount, const std::string& cacheFile, const INetworkDefinition& network, std::ostream& err) : mBatches(batches) , mCurrentBatch(0) @@ -207,7 +265,7 @@ RndInt8Calibrator::RndInt8Calibrator(int batches, std::vector& elemCount, c for (int i = 0; i < network.getNbInputs(); i++) { - auto input = network.getInput(i); + auto* input = network.getInput(i); std::vector rnd_data(elemCount[i]); std::generate_n(rnd_data.begin(), elemCount[i], gen); @@ -219,7 +277,7 @@ RndInt8Calibrator::RndInt8Calibrator(int batches, std::vector& elemCount, c } } -bool RndInt8Calibrator::getBatch(void* bindings[], const char* names[], int nbBindings) +bool RndInt8Calibrator::getBatch(void* bindings[], const char* names[], int nbBindings) noexcept { if (mCurrentBatch >= mBatches) { @@ -236,7 +294,7 @@ bool RndInt8Calibrator::getBatch(void* bindings[], const char* names[], int nbBi return true; } -const void* RndInt8Calibrator::readCalibrationCache(size_t& length) +const void* RndInt8Calibrator::readCalibrationCache(size_t& length) noexcept { mCalibrationCache.clear(); std::ifstream input(mCacheFile, std::ios::binary); @@ -248,22 +306,22 @@ const void* RndInt8Calibrator::readCalibrationCache(size_t& length) } length = mCalibrationCache.size(); - return mCalibrationCache.size() ? mCalibrationCache.data() : nullptr; + return !mCalibrationCache.empty() ? mCalibrationCache.data() : nullptr; } -bool setTensorScales(const INetworkDefinition& network, float inScales = 2.0f, float outScales = 4.0f) +bool setTensorDynamicRange(const INetworkDefinition& network, float inRange = 2.0F, float outRange = 4.0F) { - // Ensure that all layer inputs have a scale. + // Ensure that all layer inputs have a dynamic range. for (int l = 0; l < network.getNbLayers(); l++) { - auto layer = network.getLayer(l); + auto* layer = network.getLayer(l); for (int i = 0; i < layer->getNbInputs(); i++) { ITensor* input{layer->getInput(i)}; // Optional inputs are nullptr here and are from RNN layers. if (input && !input->dynamicRangeIsSet()) { - if (!input->setDynamicRange(-inScales, inScales)) + if (!input->setDynamicRange(-inRange, inRange)) { return false; } @@ -275,17 +333,17 @@ bool setTensorScales(const INetworkDefinition& network, float inScales = 2.0f, f // Optional outputs are nullptr here and are from RNN layers. if (output && !output->dynamicRangeIsSet()) { - // Pooling must have the same input and output scales. + // Pooling must have the same input and output dynamic range. if (layer->getType() == LayerType::kPOOLING) { - if (!output->setDynamicRange(-inScales, inScales)) + if (!output->setDynamicRange(-inRange, inRange)) { return false; } } else { - if (!output->setDynamicRange(-outScales, outScales)) + if (!output->setDynamicRange(-outRange, outRange)) { return false; } @@ -296,13 +354,107 @@ bool setTensorScales(const INetworkDefinition& network, float inScales = 2.0f, f return true; } +template +void sparsify(const T* values, int64_t count, int32_t k, int32_t rs, std::vector& sparseWeights) +{ + const auto c = count / (k * rs); + sparseWeights.resize(count * sizeof(T)); + auto* sparseValues = reinterpret_cast(sparseWeights.data()); + + constexpr int32_t window = 4; + constexpr int32_t nonzeros = 2; + + const int32_t crs = c * rs; + const auto getIndex = [=](int32_t ki, int32_t ci, int32_t rsi) { return ki * crs + ci * rs + rsi; }; + + for (int64_t ki = 0; ki < k; ++ki) + { + for (int64_t rsi = 0; rsi < rs; ++rsi) + { + int32_t w = 0; + int32_t nz = 0; + for (int64_t ci = 0; ci < c; ++ci) + { + const auto index = getIndex(ki, ci, rsi); + if (nz < nonzeros) + { + sparseValues[index] = values[index]; + ++nz; + } + else + { + sparseValues[index] = 0; + } + if (++w == window) + { + w = 0; + nz = 0; + } + } + } + } +} + +void sparsify(const Weights& weights, int32_t k, int32_t rs, std::vector& sparseWeights) +{ + switch (weights.type) + { + case DataType::kFLOAT: + sparsify(static_cast(weights.values), weights.count, k, rs, sparseWeights); + break; + case DataType::kHALF: + sparsify(static_cast(weights.values), weights.count, k, rs, sparseWeights); + break; + case DataType::kINT8: + case DataType::kINT32: + case DataType::kBOOL: break; + } +} + +template +void setSparseWeights(L& l, int32_t k, int32_t rs, std::vector& sparseWeights) +{ + auto weights = l.getKernelWeights(); + sparsify(weights, k, rs, sparseWeights); + weights.values = sparseWeights.data(); + l.setKernelWeights(weights); +} + +void sparsify(INetworkDefinition& network, std::vector>& sparseWeights) +{ + for (int32_t l = 0; l < network.getNbLayers(); ++l) + { + auto* layer = network.getLayer(l); + const auto t = layer->getType(); + if (t == LayerType::kCONVOLUTION) + { + auto& conv = *static_cast(layer); + const auto& dims = conv.getKernelSizeNd(); + if (dims.nbDims > 2) + { + continue; + } + const auto k = conv.getNbOutputMaps(); + const auto rs = dims.d[0] * dims.d[1]; + sparseWeights.emplace_back(); + setSparseWeights(conv, k, rs, sparseWeights.back()); + } + else if (t == LayerType::kFULLY_CONNECTED) + { + auto& fc = *static_cast(layer); + const auto k = fc.getNbOutputChannels(); + sparseWeights.emplace_back(); + setSparseWeights(fc, k, 1, sparseWeights.back()); + } + } +} + } // namespace -ICudaEngine* networkToEngine(const BuildOptions& build, const SystemOptions& sys, IBuilder& builder, - INetworkDefinition& network, std::ostream& err) +bool setupNetworkAndConfig(const BuildOptions& build, const SystemOptions& sys, IBuilder& builder, + INetworkDefinition& network, IBuilderConfig& config, std::ostream& err, + std::vector>& sparseWeights) { - TrtUniquePtr config{builder.createBuilderConfig()}; - IOptimizationProfile* profile{nullptr}; if (build.maxBatch) { @@ -317,10 +469,10 @@ ICudaEngine* networkToEngine(const BuildOptions& build, const SystemOptions& sys bool broadcastInputFormats = broadcastIOFormats(build.inputFormats, network.getNbInputs()); - for (unsigned int i = 0, n = network.getNbInputs(); i < n; i++) + for (uint32_t i = 0, n = network.getNbInputs(); i < n; i++) { // Set formats and data types of inputs - auto input = network.getInput(i); + auto* input = network.getInput(i); if (!build.inputFormats.empty()) { int inputFormatIndex = broadcastInputFormats ? 0 : i; @@ -396,48 +548,56 @@ ICudaEngine* networkToEngine(const BuildOptions& build, const SystemOptions& sys profileDims = shapes[static_cast(OptProfileSelector::kMIN)]; SMP_RETVAL_IF_FALSE(profile->setShapeValues(input->getName(), OptProfileSelector::kMIN, profileDims.data(), static_cast(profileDims.size())), - "Error in set shape values MIN", nullptr, err); + "Error in set shape values MIN", false, err); profileDims = shapes[static_cast(OptProfileSelector::kOPT)]; SMP_RETVAL_IF_FALSE(profile->setShapeValues(input->getName(), OptProfileSelector::kOPT, profileDims.data(), static_cast(profileDims.size())), - "Error in set shape values OPT", nullptr, err); + "Error in set shape values OPT", false, err); profileDims = shapes[static_cast(OptProfileSelector::kMAX)]; SMP_RETVAL_IF_FALSE(profile->setShapeValues(input->getName(), OptProfileSelector::kMAX, profileDims.data(), static_cast(profileDims.size())), - "Error in set shape values MAX", nullptr, err); + "Error in set shape values MAX", false, err); } else { profileDims = shapes[static_cast(OptProfileSelector::kMIN)]; SMP_RETVAL_IF_FALSE( profile->setDimensions(input->getName(), OptProfileSelector::kMIN, toDims(profileDims)), - "Error in set dimensions to profile MIN", nullptr, err); + "Error in set dimensions to profile MIN", false, err); profileDims = shapes[static_cast(OptProfileSelector::kOPT)]; SMP_RETVAL_IF_FALSE( profile->setDimensions(input->getName(), OptProfileSelector::kOPT, toDims(profileDims)), - "Error in set dimensions to profile OPT", nullptr, err); + "Error in set dimensions to profile OPT", false, err); profileDims = shapes[static_cast(OptProfileSelector::kMAX)]; SMP_RETVAL_IF_FALSE( profile->setDimensions(input->getName(), OptProfileSelector::kMAX, toDims(profileDims)), - "Error in set dimensions to profile MAX", nullptr, err); + "Error in set dimensions to profile MAX", false, err); } } } } + if (!hasDynamicShapes && !build.shapes.empty()) + { + sample::gLogError << "Static model does not take explicit shapes since the shape of inference tensors will be " + "determined by the model itself" + << std::endl; + return false; + } + if (profile && hasDynamicShapes) { - SMP_RETVAL_IF_FALSE(profile->isValid(), "Required optimization profile is invalid", nullptr, err); + SMP_RETVAL_IF_FALSE(profile->isValid(), "Required optimization profile is invalid", false, err); SMP_RETVAL_IF_FALSE( - config->addOptimizationProfile(profile) != -1, "Error in add optimization profile", nullptr, err); + config.addOptimizationProfile(profile) != -1, "Error in add optimization profile", false, err); } bool broadcastOutputFormats = broadcastIOFormats(build.outputFormats, network.getNbOutputs(), false); - for (unsigned int i = 0, n = network.getNbOutputs(); i < n; i++) + for (uint32_t i = 0, n = network.getNbOutputs(); i < n; i++) { // Set formats and data types of outputs - auto output = network.getOutput(i); + auto* output = network.getOutput(i); if (!build.outputFormats.empty()) { int outputFormatIndex = broadcastOutputFormats ? 0 : i; @@ -450,35 +610,44 @@ ICudaEngine* networkToEngine(const BuildOptions& build, const SystemOptions& sys } } - config->setMaxWorkspaceSize(static_cast(build.workspace) << 20); + config.setMaxWorkspaceSize(static_cast(build.workspace) << 20); - if (!build.builderCache) + if (build.timingCacheMode == TimingCacheMode::kDISABLE) { - config->setFlag(BuilderFlag::kDISABLE_TIMING_CACHE); + config.setFlag(BuilderFlag::kDISABLE_TIMING_CACHE); } if (!build.tf32) { - config->clearFlag(BuilderFlag::kTF32); - } - - config->setProfilingVerbosity(build.nvtxMode); - config->setMinTimingIterations(build.minTiming); - config->setAvgTimingIterations(build.avgTiming); - - if (build.fp16) - { - config->setFlag(BuilderFlag::kFP16); - } - - if (build.int8) - { - config->setFlag(BuilderFlag::kINT8); + config.clearFlag(BuilderFlag::kTF32); } if (build.refittable) { - config->setFlag(BuilderFlag::kREFIT); + config.setFlag(BuilderFlag::kREFIT); + } + + if (build.sparsity != SparsityFlag::kDISABLE) + { + config.setFlag(BuilderFlag::kSPARSE_WEIGHTS); + if (build.sparsity == SparsityFlag::kFORCE) + { + sparsify(network, sparseWeights); + } + } + + config.setProfilingVerbosity(build.nvtxMode); + config.setMinTimingIterations(build.minTiming); + config.setAvgTimingIterations(build.avgTiming); + + if (build.fp16) + { + config.setFlag(BuilderFlag::kFP16); + } + + if (build.int8) + { + config.setFlag(BuilderFlag::kINT8); } if (build.int8 && !build.fp16) @@ -493,21 +662,50 @@ ICudaEngine* networkToEngine(const BuildOptions& build, const SystemOptions& sys auto int8IO = std::count_if(build.inputFormats.begin(), build.inputFormats.end(), isInt8) + std::count_if(build.outputFormats.begin(), build.outputFormats.end(), isInt8); - if ((build.int8 && build.calibration.empty()) || int8IO) + auto hasQDQLayers = [](INetworkDefinition& network) { + // Determine if our network has QDQ layers. + const auto nbLayers = network.getNbLayers(); + for (int32_t i = 0; i < nbLayers; i++) + { + const auto& layer = network.getLayer(i); + if (layer->getType() == LayerType::kQUANTIZE || layer->getType() == LayerType::kDEQUANTIZE) + { + return true; + } + } + return false; + }; + + if (!hasQDQLayers(network) && (build.int8 || int8IO) && build.calibration.empty()) { // Explicitly set int8 scales if no calibrator is provided and if I/O tensors use int8, // because auto calibration does not support this case. - SMP_RETVAL_IF_FALSE(setTensorScales(network), "Error in set tensor scales.", nullptr, err); + SMP_RETVAL_IF_FALSE(setTensorDynamicRange(network), "Error in set tensor dynamic range.", false, err); } else if (build.int8) { + if (!hasQDQLayers(network) && int8IO) + { + try + { + // Set dynamic ranges of int8 inputs / outputs to match scales loaded from calibration cache + setTensorScalesFromCalibration(network, build.inputFormats, build.outputFormats, build.calibration); + } + catch (std::exception&) + { + sample::gLogError + << "Int8IO was specified but impossible to read tensor scales from provided calibration cache file" + << std::endl; + return false; + } + } IOptimizationProfile* profileCalib{nullptr}; if (!build.shapesCalib.empty()) { profileCalib = builder.createOptimizationProfile(); - for (unsigned int i = 0, n = network.getNbInputs(); i < n; i++) + for (uint32_t i = 0, n = network.getNbInputs(); i < n; i++) { - auto input = network.getInput(i); + auto* input = network.getInput(i); Dims profileDims{}; auto shape = build.shapesCalib.find(input->getName()); ShapeRange shapesCalib{}; @@ -517,24 +715,24 @@ ICudaEngine* networkToEngine(const BuildOptions& build, const SystemOptions& sys // Here we check only kMIN as all profileDims are the same. SMP_RETVAL_IF_FALSE( profileCalib->setDimensions(input->getName(), OptProfileSelector::kMIN, profileDims), - "Error in set dimensions to calibration profile OPT", nullptr, err); + "Error in set dimensions to calibration profile OPT", false, err); profileCalib->setDimensions(input->getName(), OptProfileSelector::kOPT, profileDims); profileCalib->setDimensions(input->getName(), OptProfileSelector::kMAX, profileDims); } - SMP_RETVAL_IF_FALSE(profileCalib->isValid(), "Calibration profile is invalid", nullptr, err); + SMP_RETVAL_IF_FALSE(profileCalib->isValid(), "Calibration profile is invalid", false, err); SMP_RETVAL_IF_FALSE( - config->setCalibrationProfile(profileCalib), "Error in set calibration profile", nullptr, err); + config.setCalibrationProfile(profileCalib), "Error in set calibration profile", false, err); } - std::vector elemCount{}; + std::vector elemCount{}; for (int i = 0; i < network.getNbInputs(); i++) { - auto input = network.getInput(i); + auto* input = network.getInput(i); if (profileCalib) { elemCount.push_back(volume(profileCalib->getDimensions(input->getName(), OptProfileSelector::kOPT))); } - else if (profile && (profile->getDimensions(input->getName(), OptProfileSelector::kOPT).nbDims >= 0)) + else if (profile && hasDynamicShapes) { elemCount.push_back(volume(profile->getDimensions(input->getName(), OptProfileSelector::kOPT))); } @@ -544,86 +742,190 @@ ICudaEngine* networkToEngine(const BuildOptions& build, const SystemOptions& sys } } - config->setInt8Calibrator(new RndInt8Calibrator(1, elemCount, build.calibration, network, err)); + config.setInt8Calibrator(new RndInt8Calibrator(1, elemCount, build.calibration, network, err)); } if (build.safe) { - config->setEngineCapability(sys.DLACore != -1 ? EngineCapability::kSAFE_DLA : EngineCapability::kSAFE_GPU); + config.setEngineCapability(sys.DLACore != -1 ? EngineCapability::kSAFE_DLA : EngineCapability::kSAFE_GPU); } if (sys.DLACore != -1) { if (sys.DLACore < builder.getNbDLACores()) { - config->setDefaultDeviceType(DeviceType::kDLA); - config->setDLACore(sys.DLACore); - config->setFlag(BuilderFlag::kSTRICT_TYPES); + config.setDefaultDeviceType(DeviceType::kDLA); + config.setDLACore(sys.DLACore); + config.setFlag(BuilderFlag::kSTRICT_TYPES); if (sys.fallback) { - config->setFlag(BuilderFlag::kGPU_FALLBACK); + config.setFlag(BuilderFlag::kGPU_FALLBACK); } if (!build.int8) { - config->setFlag(BuilderFlag::kFP16); + config.setFlag(BuilderFlag::kFP16); } } else { err << "Cannot create DLA engine, " << sys.DLACore << " not available" << std::endl; - return nullptr; + return false; } } if (build.enabledTactics || build.disabledTactics) { - TacticSources tacticSources = config->getTacticSources(); + TacticSources tacticSources = config.getTacticSources(); tacticSources |= build.enabledTactics; tacticSources &= ~build.disabledTactics; - config->setTacticSources(tacticSources); + config.setTacticSources(tacticSources); } - return builder.buildEngineWithConfig(network, *config); + return true; } -ICudaEngine* modelToEngine( +//! +//! \brief Create an engine for a network defintion +//! +//! \return Pointer to the engine created or nullptr if the creation failed +//! +TrtUniquePtr networkToEngine(const BuildOptions& build, const SystemOptions& sys, IBuilder& builder, + INetworkDefinition& network, std::ostream& err) +{ + TrtUniquePtr config{builder.createBuilderConfig()}; + TrtUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + std::vector> sparseWeights; + SMP_RETVAL_IF_FALSE(config != nullptr, "Config creation failed", nullptr, err); + SMP_RETVAL_IF_FALSE(runtime != nullptr, "Runtime creation failed", nullptr, err); + SMP_RETVAL_IF_FALSE(setupNetworkAndConfig(build, sys, builder, network, *config, err, sparseWeights), + "Network And Config setup failed", nullptr, err); + runtime->setErrorRecorder(&gRecorder); + + std::unique_ptr timingCache{nullptr}; + // Try to load cache from file. Create a fresh cache if the file doesn't exist + if (build.timingCacheMode == TimingCacheMode::kGLOBAL) + { + std::vector loadedCache = loadTimingCacheFile(build.timingCacheFile); + timingCache.reset(config->createTimingCache(static_cast(loadedCache.data()), loadedCache.size())); + SMP_RETVAL_IF_FALSE(timingCache != nullptr, "TimingCache creation failed", nullptr, err); + config->setTimingCache(*timingCache, false); + } + + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + SMP_RETVAL_IF_FALSE(profileStream != nullptr, "Cuda stream creation failed", nullptr, err); + config->setProfileStream(*profileStream); + + TrtUniquePtr plan{builder.buildSerializedNetwork(network, *config)}; + ICudaEngine* engine{runtime->deserializeCudaEngine(plan->data(), plan->size())}; + SMP_RETVAL_IF_FALSE(engine != nullptr, "Engine creation failed", nullptr, err); + if (build.timingCacheMode == TimingCacheMode::kGLOBAL) + { + auto timingCache = config->getTimingCache(); + std::unique_ptr timingCacheHostData{timingCache->serialize()}; + SMP_RETVAL_IF_FALSE(timingCacheHostData != nullptr, "Timing Cache serialization failed", nullptr, err); + saveTimingCacheFile(build.timingCacheFile, timingCacheHostData.get()); + } + if (config->getInt8Calibrator()) + { + delete config->getInt8Calibrator(); + } + return TrtUniquePtr(engine); +} + +//! +//! \brief Parse a given model, create a network and an engine. +//! +std::tuple, TrtUniquePtr, Parser> modelToEngineNetworkParserTuple( const ModelOptions& model, const BuildOptions& build, const SystemOptions& sys, std::ostream& err) { TrtUniquePtr builder{createInferBuilder(sample::gLogger.getTRTLogger())}; if (builder == nullptr) { err << "Builder creation failed" << std::endl; - return nullptr; + return {}; } - auto batchFlag + builder->setErrorRecorder(&gRecorder); + auto networkFlags = (build.maxBatch) ? 0U : 1U << static_cast(nvinfer1::NetworkDefinitionCreationFlag::kEXPLICIT_BATCH); - TrtUniquePtr network{builder->createNetworkV2(batchFlag)}; + if (build.explicitPrecision) + { + networkFlags |= 1U << static_cast(NetworkDefinitionCreationFlag::kEXPLICIT_PRECISION); + } + + TrtUniquePtr network{builder->createNetworkV2(networkFlags)}; if (!network) { err << "Network creation failed" << std::endl; - return nullptr; + return {}; } Parser parser = modelToNetwork(model, *network, err); if (!parser) { err << "Parsing model failed" << std::endl; - return nullptr; + return {}; } - return networkToEngine(build, sys, *builder, *network, err); + auto engine = networkToEngine(build, sys, *builder, *network, err); + return std::make_tuple(std::move(engine), std::move(network), std::move(parser)); } +namespace +{ +std::pair, std::vector> getLayerWeightsRolePair(IRefitter& refitter) +{ + // Get number of refittable items. + auto const nbAll = refitter.getAll(0, nullptr, nullptr); + std::vector layerNames(nbAll); + // Allocate buffers for the items and get them. + std::vector weightsRoles(nbAll); + refitter.getAll(nbAll, layerNames.data(), weightsRoles.data()); + std::vector layerNameStrs(nbAll); + std::transform(layerNames.begin(), layerNames.end(), layerNameStrs.begin(), [](char const* name) { + if (name == nullptr) + { + return std::string{}; + } + return std::string{name}; + }); + return {layerNameStrs, weightsRoles}; +} + +std::pair, std::vector> getMissingLayerWeightsRolePair(IRefitter& refitter) +{ + // Get number of refittable items. + auto const nbMissing = refitter.getMissing(0, nullptr, nullptr); + std::vector layerNames(nbMissing); + // Allocate buffers for the items and get them. + std::vector weightsRoles(nbMissing); + refitter.getMissing(nbMissing, layerNames.data(), weightsRoles.data()); + std::vector layerNameStrs(nbMissing); + std::transform(layerNames.begin(), layerNames.end(), layerNameStrs.begin(), [](char const* name) { + if (name == nullptr) + { + return std::string{}; + } + return std::string{name}; + }); + return {layerNameStrs, weightsRoles}; +} +} // namespace + void dumpRefittable(nvinfer1::ICudaEngine& engine) { TrtUniquePtr refitter{createInferRefitter(engine, sample::gLogger.getTRTLogger())}; - // Get number of refittable items. - const int nbAll = refitter->getAll(0, nullptr, nullptr); - std::vector layerNames(nbAll); - // Allocate buffers for the items and get them. - std::vector weightsRoles(nbAll); - refitter->getAll(nbAll, layerNames.data(), weightsRoles.data()); - for (int i = 0; i < nbAll; ++i) + if (refitter == nullptr) + { + sample::gLogError << "Failed to create a refitter." << std::endl; + return; + } + auto const& layerWeightsRolePair = getLayerWeightsRolePair(*refitter); + + auto const& layerNames = layerWeightsRolePair.first; + auto const& weightsRoles = layerWeightsRolePair.second; + auto const nbAll = layerWeightsRolePair.first.size(); + for (size_t i = 0; i < nbAll; ++i) { sample::gLogInfo << layerNames[i] << " " << weightsRoles[i] << std::endl; } @@ -638,9 +940,9 @@ ICudaEngine* loadEngine(const std::string& engine, int DLACore, std::ostream& er return nullptr; } - engineFile.seekg(0, engineFile.end); + engineFile.seekg(0, std::ifstream::end); long int fsize = engineFile.tellg(); - engineFile.seekg(0, engineFile.beg); + engineFile.seekg(0, std::ifstream::beg); std::vector engineData(fsize); engineFile.read(engineData.data(), fsize); @@ -655,6 +957,7 @@ ICudaEngine* loadEngine(const std::string& engine, int DLACore, std::ostream& er { runtime->setDLACore(DLACore); } + runtime->setErrorRecorder(&gRecorder); return runtime->deserializeCudaEngine(engineData.data(), fsize, nullptr); } @@ -679,29 +982,224 @@ bool saveEngine(const ICudaEngine& engine, const std::string& fileName, std::ost return !engineFile.fail(); } -TrtUniquePtr getEngine( +std::tuple, TrtUniquePtr, Parser> getEngineNetworkParserTuple( const ModelOptions& model, const BuildOptions& build, const SystemOptions& sys, std::ostream& err) { TrtUniquePtr engine; + TrtUniquePtr network; + Parser parser; if (build.load) { engine.reset(loadEngine(build.engine, sys.DLACore, err)); } else { - engine.reset(modelToEngine(model, build, sys, err)); + std::tie(engine, network, parser) = modelToEngineNetworkParserTuple(model, build, sys, err); } if (!engine) { err << "Engine creation failed" << std::endl; - return nullptr; + return {}; } if (build.save && !saveEngine(*engine, build.engine, err)) { err << "Saving engine to file failed" << std::endl; - return nullptr; + return {}; } - return engine; + return std::make_tuple(std::move(engine), std::move(network), std::move(parser)); +} + +IHostMemory* networkToSerialized(const BuildOptions& build, const SystemOptions& sys, IBuilder& builder, + INetworkDefinition& network, std::ostream& err) +{ + TrtUniquePtr config{builder.createBuilderConfig()}; + std::vector> sparseWeights; + SMP_RETVAL_IF_FALSE(config != nullptr, "Config creation failed", nullptr, err); + SMP_RETVAL_IF_FALSE(setupNetworkAndConfig(build, sys, builder, network, *config, err, sparseWeights), + "Network And Config setup failed", nullptr, err); + return builder.buildSerializedNetwork(network, *config); +} + +IHostMemory* modelToSerialized( + const ModelOptions& model, const BuildOptions& build, const SystemOptions& sys, std::ostream& err) +{ + TrtUniquePtr builder{createInferBuilder(sample::gLogger.getTRTLogger())}; + SMP_RETVAL_IF_FALSE(builder != nullptr, "Builder creation failed", nullptr, err); + builder->setErrorRecorder(&gRecorder); + + auto networkFlags + = (build.maxBatch) ? 0U : 1U << static_cast(nvinfer1::NetworkDefinitionCreationFlag::kEXPLICIT_BATCH); + if (build.explicitPrecision) + { + networkFlags |= 1U << static_cast(NetworkDefinitionCreationFlag::kEXPLICIT_PRECISION); + } + + TrtUniquePtr network{builder->createNetworkV2(networkFlags)}; + SMP_RETVAL_IF_FALSE(network != nullptr, "Network creation failed", nullptr, err); + + Parser parser = modelToNetwork(model, *network, err); + SMP_RETVAL_IF_FALSE(parser, "Parsing model failed", nullptr, err); + + return networkToSerialized(build, sys, *builder, *network, err); +} + +bool serializeAndSave(const ModelOptions& model, const BuildOptions& build, const SystemOptions& sys, std::ostream& err) +{ + TrtUniquePtr serialized{modelToSerialized(model, build, sys, err)}; + SMP_RETVAL_IF_FALSE(serialized != nullptr, "Network serialization failed", false, err); + + std::ofstream engineFile(build.engine, std::ios::binary); + SMP_RETVAL_IF_FALSE(!!engineFile, "Cannot open a file to save a serialize network", false, err); + engineFile.write(static_cast(serialized->data()), serialized->size()); + return !engineFile.fail(); +} + +// There is not a getWeightsName API, so we need to use WeightsRole. +std::vector> getAllRefitWeightsForLayer(const ILayer& l) +{ + switch (l.getType()) + { + case LayerType::kCONSTANT: + { + const auto& layer = static_cast(l); + return {std::make_pair(WeightsRole::kCONSTANT, layer.getWeights())}; + } + case LayerType::kCONVOLUTION: + { + const auto& layer = static_cast(l); + return {std::make_pair(WeightsRole::kKERNEL, layer.getKernelWeights()), + std::make_pair(WeightsRole::kBIAS, layer.getBiasWeights())}; + } + case LayerType::kDECONVOLUTION: + { + const auto& layer = static_cast(l); + return {std::make_pair(WeightsRole::kKERNEL, layer.getKernelWeights()), + std::make_pair(WeightsRole::kBIAS, layer.getBiasWeights())}; + } + case LayerType::kFULLY_CONNECTED: + { + const auto& layer = static_cast(l); + return {std::make_pair(WeightsRole::kKERNEL, layer.getKernelWeights()), + std::make_pair(WeightsRole::kBIAS, layer.getBiasWeights())}; + } + case LayerType::kSCALE: + { + const auto& layer = static_cast(l); + return {std::make_pair(WeightsRole::kSCALE, layer.getScale()), + std::make_pair(WeightsRole::kSHIFT, layer.getShift())}; + } + case LayerType::kRNN_V2: + case LayerType::kACTIVATION: + case LayerType::kPOOLING: + case LayerType::kLRN: + case LayerType::kSOFTMAX: + case LayerType::kSHUFFLE: + case LayerType::kCONCATENATION: + case LayerType::kELEMENTWISE: + case LayerType::kPLUGIN: + case LayerType::kUNARY: + case LayerType::kPADDING: + case LayerType::kREDUCE: + case LayerType::kTOPK: + case LayerType::kGATHER: + case LayerType::kMATRIX_MULTIPLY: + case LayerType::kRAGGED_SOFTMAX: + case LayerType::kIDENTITY: + case LayerType::kPLUGIN_V2: + case LayerType::kSLICE: + case LayerType::kFILL: + case LayerType::kSHAPE: + case LayerType::kPARAMETRIC_RELU: + case LayerType::kRESIZE: + case LayerType::kTRIP_LIMIT: + case LayerType::kRECURRENCE: + case LayerType::kITERATOR: + case LayerType::kLOOP_OUTPUT: + case LayerType::kSELECT: + case LayerType::kQUANTIZE: + case LayerType::kDEQUANTIZE: return {}; + } + return {}; +} + +bool timeRefit(INetworkDefinition const& network, nvinfer1::ICudaEngine& engine) +{ + using time_point = std::chrono::time_point; + using durationMs = std::chrono::duration; + + auto const nbLayers = network.getNbLayers(); + TrtUniquePtr refitter{createInferRefitter(engine, sample::gLogger.getTRTLogger())}; + auto const& layerWeightsRolePair = getLayerWeightsRolePair(*refitter); + // We use std::string instead of const char* since we can have copies of layer names. + std::set> layerRoleSet; + + auto const& layerNames = layerWeightsRolePair.first; + auto const& weightsRoles = layerWeightsRolePair.second; + + std::transform(layerNames.begin(), layerNames.end(), weightsRoles.begin(), + std::inserter(layerRoleSet, layerRoleSet.begin()), + [](std::string const& layerName, WeightsRole const role) { return std::make_pair(layerName, role); }); + + auto const isRefittable = [&layerRoleSet](char const* layerName, WeightsRole const role) { + return layerRoleSet.find(std::make_pair(layerName, role)) != layerRoleSet.end(); + }; + + auto const setWeights = [&] { + for (int32_t i = 0; i < nbLayers; i++) + { + auto const layer = network.getLayer(i); + auto const roleWeightsVec = getAllRefitWeightsForLayer(*layer); + for (auto const& roleWeights : roleWeightsVec) + { + if (isRefittable(layer->getName(), roleWeights.first)) + { + bool const success = refitter->setWeights(layer->getName(), roleWeights.first, roleWeights.second); + if (!success) + { + return false; + } + } + } + } + return true; + }; + + auto const reportMissingWeights = [&] { + auto const& missingPair = getMissingLayerWeightsRolePair(*refitter); + auto const& layerNames = missingPair.first; + auto const& weightsRoles = missingPair.second; + for (size_t i = 0; i < layerNames.size(); ++i) + { + sample::gLogError << "Missing (" << layerNames[i] << ", " << weightsRoles[i] << ") for refitting." + << std::endl; + } + return layerNames.empty(); + }; + + // Warm up and report missing weights + bool const success = setWeights() && reportMissingWeights() && refitter->refitCudaEngine(); + if (!success) + { + return false; + } + + constexpr int32_t loop = 10; + time_point const refitStartTime{std::chrono::steady_clock::now()}; + { + for (int32_t l = 0; l < loop; l++) + { + bool const success = setWeights() && refitter->refitCudaEngine(); + if (!success) + { + return false; + } + } + } + time_point const refitEndTime{std::chrono::steady_clock::now()}; + + sample::gLogInfo << "Engine refitted" + << " in " << durationMs(refitEndTime - refitStartTime).count() / loop << " ms." << std::endl; + return true; } } // namespace sample diff --git a/samples/common/sampleEngines.h b/samples/common/sampleEngines.h index ed9ac6b9..0425bb47 100644 --- a/samples/common/sampleEngines.h +++ b/samples/common/sampleEngines.h @@ -18,6 +18,7 @@ #define TRT_SAMPLE_ENGINES_H #include +#include #include "NvCaffeParser.h" #include "NvInfer.h" @@ -53,20 +54,13 @@ struct Parser Parser modelToNetwork(const ModelOptions& model, nvinfer1::INetworkDefinition& network, std::ostream& err); //! -//! \brief Create an engine for a network defintion +//! \brief Set up network and config //! -//! \return Pointer to the engine created or nullptr if the creation failed +//! \return boolean Return true if network and config were successfully set //! -nvinfer1::ICudaEngine* networkToEngine(const BuildOptions& build, const SystemOptions& sys, nvinfer1::IBuilder& builder, - nvinfer1::INetworkDefinition& network, std::ostream& err); - -//! -//! \brief Create an engine for a given model -//! -//! \return Pointer to the engine created or nullptr if the creation failed -//! -nvinfer1::ICudaEngine* modelToEngine( - const ModelOptions& model, const BuildOptions& build, const SystemOptions& sys, std::ostream& err); +bool setupNetworkAndConfig(const BuildOptions& build, const SystemOptions& sys, IBuilder& builder, + INetworkDefinition& network, IBuilderConfig& config, std::ostream& err, + std::vector>& sparseWeights); //! //! \brief Log refittable layers and weights of a refittable engine @@ -92,9 +86,51 @@ bool saveEngine(const nvinfer1::ICudaEngine& engine, const std::string& fileName //! //! \return Pointer to the engine created or nullptr if the creation failed //! -TrtUniquePtr getEngine( +std::tuple, TrtUniquePtr, Parser> getEngineNetworkParserTuple( const ModelOptions& model, const BuildOptions& build, const SystemOptions& sys, std::ostream& err); +//! +//! \brief Create an engine from model or serialized file, and optionally save engine +//! +//! \return Pointer to the engine created or nullptr if the creation failed +//! +inline TrtUniquePtr getEngine( + const ModelOptions& model, const BuildOptions& build, const SystemOptions& sys, std::ostream& err) +{ + return std::get<0>(getEngineNetworkParserTuple(model, build, sys, err)); +} + +//! +//! \brief Create a serialized network +//! +//! \return Pointer to a host memory for a serialized network +//! +IHostMemory* networkToSerialized(const BuildOptions& build, const SystemOptions& sys, IBuilder& builder, + INetworkDefinition& network, std::ostream& err); + +//! +//! \brief Tranfer model to a serialized network +//! +//! \return Pointer to a host memory for a serialized network +//! +IHostMemory* modelToSerialized( + const ModelOptions& model, const BuildOptions& build, const SystemOptions& sys, std::ostream& err); + +//! +//! \brief Serialize network and save it into a file +//! +//! \return boolean Return true if the network was successfully serialized and saved +//! +bool serializeAndSave(const ModelOptions& model, const BuildOptions& build, const SystemOptions& sys, std::ostream& err); + +bool timeRefit(const INetworkDefinition& network, nvinfer1::ICudaEngine& engine); + +//! +//! \brief Set tensor scales from a calibration table +//! +void setTensorScalesFromCalibration(nvinfer1::INetworkDefinition& network, const std::vector& inputFormats, + const std::vector& outputFormats, const std::string& calibrationFile); + } // namespace sample #endif // TRT_SAMPLE_ENGINES_H diff --git a/samples/common/sampleInference.cpp b/samples/common/sampleInference.cpp index e7f2d249..eb619544 100644 --- a/samples/common/sampleInference.cpp +++ b/samples/common/sampleInference.cpp @@ -148,6 +148,15 @@ bool setUpInference(InferenceEnvironment& iEnv, const InferenceOptions& inferenc { bindings->addBinding(b, name, isInput, vol, dataType); } + + if (isInput) + { + sample::gLogInfo << "Created input binding for " << name << " with dimensions " << dims << std::endl; + } + else + { + sample::gLogInfo << "Created output binding for " << name << " with dimensions " << dims << std::endl; + } } } @@ -198,9 +207,9 @@ public: { } - void operator()(TrtCudaStream& stream) const + bool operator()(TrtCudaStream& stream) const { - mContext.enqueue(mBatch, mBuffers, stream.get(), nullptr); + return mContext.enqueue(mBatch, mBuffers, stream.get(), nullptr); } private: @@ -220,9 +229,9 @@ public: { } - void operator()(TrtCudaStream& stream) const + bool operator()(TrtCudaStream& stream) const { - mContext.enqueueV2(mBuffers, stream.get(), nullptr); + return mContext.enqueueV2(mBuffers, stream.get(), nullptr); } }; @@ -239,15 +248,15 @@ public: { } - void operator()(TrtCudaStream& stream) const + bool operator()(TrtCudaStream& stream) const { - mGraph.launch(stream); + return mGraph.launch(stream); } TrtCudaGraph& mGraph; }; -using EnqueueFunction = std::function; +using EnqueueFunction = std::function; enum class StreamType : int { @@ -300,11 +309,11 @@ public: createEnqueueFunction(inference, context, bindings); } - void query(bool skipTransfers) + bool query(bool skipTransfers) { if (mActive[mNext]) { - return; + return true; } if (!skipTransfers) @@ -317,7 +326,10 @@ public: record(EventType::kCOMPUTE_S, StreamType::kCOMPUTE); recordEnqueueTime(); - mEnqueue(getStream(StreamType::kCOMPUTE)); + if (!mEnqueue(getStream(StreamType::kCOMPUTE))) + { + return false; + } recordEnqueueTime(); record(EventType::kCOMPUTE_E, StreamType::kCOMPUTE); @@ -331,6 +343,7 @@ public: mActive[mNext] = true; moveNext(); + return true; } float sync( @@ -425,6 +438,7 @@ private: = skipTransfers ? getEvent(EventType::kCOMPUTE_E) - gpuStart : getEvent(EventType::kOUTPUT_S) - gpuStart; float oe = skipTransfers ? getEvent(EventType::kCOMPUTE_E) - gpuStart : getEvent(EventType::kOUTPUT_E) - gpuStart; + return InferenceTrace(mStreamId, std::chrono::duration(getEnqueueTime(true) - cpuStart).count(), std::chrono::duration(getEnqueueTime(false) - cpuStart).count(), is, ie, @@ -445,12 +459,32 @@ private: if (inference.graph) { TrtCudaStream& stream = getStream(StreamType::kCOMPUTE); - mEnqueue(stream); + // Avoid capturing initialization calls by executing the enqueue function at least + // once before starting CUDA graph capture. + const auto ret = mEnqueue(stream); + assert(ret); stream.synchronize(); + mGraph.beginCapture(stream); - mEnqueue(stream); - mGraph.endCapture(stream); - mEnqueue = EnqueueFunction(EnqueueGraph(mGraph)); + // The built TRT engine may contain operations that are not permitted under CUDA graph capture mode. + // When the stream is capturing, the enqueue call may return false if the current CUDA graph capture fails. + if (mEnqueue(stream)) + { + mGraph.endCapture(stream); + mEnqueue = EnqueueFunction(EnqueueGraph(mGraph)); + } + else + { + mGraph.endCaptureOnError(stream); + // Ensure any CUDA error has been cleaned up. + cudaCheck(cudaGetLastError()); + sample::gLogWarning << "The built TensorRT engine contains operations that are not permitted under " + "CUDA graph capture mode." + << std::endl; + sample::gLogWarning << "The specified --useCudaGraph flag has been ignored. The inference will be " + "launched without using CUDA graph launch." + << std::endl; + } } } @@ -473,7 +507,7 @@ private: using IterationStreams = std::vector>; -void inferenceLoop(IterationStreams& iStreams, const TimePoint& cpuStart, const TrtCudaEvent& gpuStart, int iterations, +bool inferenceLoop(IterationStreams& iStreams, const TimePoint& cpuStart, const TrtCudaEvent& gpuStart, int iterations, float maxDurationMs, float warmupMs, std::vector& trace, bool skipTransfers) { float durationMs = 0; @@ -483,7 +517,10 @@ void inferenceLoop(IterationStreams& iStreams, const TimePoint& cpuStart, const { for (auto& s : iStreams) { - s->query(skipTransfers); + if (!s->query(skipTransfers)) + { + return false; + } } for (auto& s : iStreams) { @@ -502,6 +539,7 @@ void inferenceLoop(IterationStreams& iStreams, const TimePoint& cpuStart, const { s->syncAll(cpuStart, gpuStart, trace, skipTransfers); } + return true; } void inferenceExecution(const InferenceOptions& inference, InferenceEnvironment& iEnv, SyncStruct& sync, int offset, @@ -529,8 +567,11 @@ void inferenceExecution(const InferenceOptions& inference, InferenceEnvironment& } std::vector localTrace; - inferenceLoop(iStreams, sync.cpuStart, sync.gpuStart, inference.iterations, durationMs, warmupMs, localTrace, - inference.skipTransfers); + if (!inferenceLoop(iStreams, sync.cpuStart, sync.gpuStart, inference.iterations, durationMs, warmupMs, localTrace, + inference.skipTransfers)) + { + iEnv.error = true; + } if (inference.skipTransfers) { @@ -554,7 +595,7 @@ inline std::thread makeThread(const InferenceOptions& inference, InferenceEnviro } // namespace -void runInference( +bool runInference( const InferenceOptions& inference, InferenceEnvironment& iEnv, int device, std::vector& trace) { trace.resize(0); @@ -578,8 +619,84 @@ void runInference( th.join(); } - auto cmpTrace = [](const InferenceTrace& a, const InferenceTrace& b) { return a.inStart < b.inStart; }; + auto cmpTrace = [](const InferenceTrace& a, const InferenceTrace& b) { return a.h2dStart < b.h2dStart; }; std::sort(trace.begin(), trace.end(), cmpTrace); -} + return !iEnv.error; +} +namespace +{ +size_t reportGpuMemory() +{ + static size_t prevFree{0}; + size_t free{0}; + size_t total{0}; + size_t newlyAllocated{0}; + cudaCheck(cudaMemGetInfo(&free, &total)); + sample::gLogInfo << "Free GPU memory = " << free / 1024.0_MiB << " GiB"; + if (prevFree != 0) + { + newlyAllocated = (prevFree - free); + sample::gLogInfo << ", newly allocated GPU memory = " << newlyAllocated / 1024.0_MiB << " GiB"; + } + sample::gLogInfo << ", total GPU memory = " << total / 1024.0_MiB << " GiB" << std::endl; + prevFree = free; + return newlyAllocated; +} +} // namespace + +//! Returns true if deserialization is slower than expected or fails. +bool timeDeserialize(InferenceEnvironment& iEnv) +{ + TrtUniquePtr rt{createInferRuntime(sample::gLogger.getTRTLogger())}; + constexpr int32_t kNB_ITERS{20}; + TrtUniquePtr engine; + TrtUniquePtr serializedEngine{iEnv.engine->serialize()}; + + sample::gLogInfo << "Begin deserialization engine..." << std::endl; + auto startClock = std::chrono::high_resolution_clock::now(); + engine.reset(rt->deserializeCudaEngine(serializedEngine->data(), serializedEngine->size(), nullptr)); + auto endClock = std::chrono::high_resolution_clock::now(); + auto const first = std::chrono::duration(endClock - startClock).count(); + sample::gLogInfo << "First deserialization time = " << first << " milliseconds" << std::endl; + + // Check if first deserialization suceeded. + if (engine == nullptr) + { + sample::gLogError << "Engine deserialization failed." << std::endl; + return true; + } + + // Record initial gpu memory state. + reportGpuMemory(); + + float totalTime{0.F}; + for (int32_t i = 0; i < kNB_ITERS; ++i) + { + engine.reset(nullptr); + + startClock = std::chrono::high_resolution_clock::now(); + engine.reset(rt->deserializeCudaEngine(serializedEngine->data(), serializedEngine->size(), nullptr)); + endClock = std::chrono::high_resolution_clock::now(); + totalTime += std::chrono::duration(endClock - startClock).count(); + } + const auto averageTime = totalTime / kNB_ITERS; + // reportGpuMemory sometimes reports zero after a single deserialization of a small engine, + // so use the size of memory for all the iterations. + const auto totalEngineSizeGpu = reportGpuMemory(); + sample::gLogInfo << "Total deserialization time = " << totalTime << " milliseconds, average time = " << averageTime + << ", first time = " << first << "." << std::endl; + sample::gLogInfo << "Deserialization Bandwidth = " << 1E-6 * totalEngineSizeGpu / totalTime << " GB/s" << std::endl; + + // If the first deserialization is more than tolerance slower than + // the average deserialization, return true, which means an error occurred. + const auto tolerance = 1.50F; + const bool isSlowerThanExpected = first > averageTime * tolerance; + if (isSlowerThanExpected) + { + sample::gLogInfo << "First deserialization time divided by average time is " << (first / averageTime) + << ". Exceeds tolerance of " << tolerance << "x." << std::endl; + } + return isSlowerThanExpected; +} } // namespace sample diff --git a/samples/common/sampleInference.h b/samples/common/sampleInference.h index bf04cf64..ff8109a0 100644 --- a/samples/common/sampleInference.h +++ b/samples/common/sampleInference.h @@ -36,6 +36,7 @@ struct InferenceEnvironment std::unique_ptr profiler; std::vector> context; std::vector> bindings; + bool error{false}; }; //! @@ -44,10 +45,14 @@ struct InferenceEnvironment bool setUpInference(InferenceEnvironment& iEnv, const InferenceOptions& inference); //! -//! \brief Run inference and collect timing +//! \brief Deserialize the engine and time how long it takes. //! -void runInference( - const InferenceOptions& inference, InferenceEnvironment& iEnv, int device, std::vector& trace); +bool timeDeserialize(InferenceEnvironment& iEnv); + +//! +//! \brief Run inference and collect timing, return false if any error hit during inference +//! +bool runInference(const InferenceOptions& inference, InferenceEnvironment& iEnv, int device, std::vector& trace); } // namespace sample diff --git a/samples/common/sampleOptions.cpp b/samples/common/sampleOptions.cpp index ed57fea2..620c87cc 100644 --- a/samples/common/sampleOptions.cpp +++ b/samples/common/sampleOptions.cpp @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -174,10 +175,13 @@ const char* boolToEnabled(bool enable) return enable ? "Enabled" : "Disabled"; } +//! Check if input option exists in input arguments. +//! If it does: return its value, erase the argument and return true. +//! If it does not: return false. template -bool checkEraseOption(Arguments& arguments, const std::string& option, T& value) +bool getAndDelOption(Arguments& arguments, const std::string& option, T& value) { - auto match = arguments.find(option); + const auto match = arguments.find(option); if (match != arguments.end()) { value = stringToValue(match->second); @@ -188,12 +192,13 @@ bool checkEraseOption(Arguments& arguments, const std::string& option, T& value) return false; } -// Like checkEraseOption, but sets value to false if arguments contain the option. -// This function should be used for options that default to true. -bool checkEraseNegativeOption(Arguments& arguments, const std::string& option, bool& value) +//! Check if input option exists in input arguments. +//! If it does: return false in value, erase the argument and return true. +//! If it does not: return false. +bool getAndDelNegOption(Arguments& arguments, const std::string& option, bool& value) { bool dummy; - if (checkEraseOption(arguments, option, dummy)) + if (getAndDelOption(arguments, option, dummy)) { value = false; return true; @@ -201,50 +206,52 @@ bool checkEraseNegativeOption(Arguments& arguments, const std::string& option, b return false; } +//! Check if input option exists in input arguments. +//! If it does: add all the matched arg values to values vector, erase the argument and return true. +//! If it does not: return false. template -bool checkEraseRepeatedOption(Arguments& arguments, const std::string& option, std::vector& values) +bool getAndDelRepeatedOption(Arguments& arguments, const std::string& option, std::vector& values) { - auto match = arguments.equal_range(option); + const auto match = arguments.equal_range(option); if (match.first == match.second) { return false; } - auto addValue = [&values](Arguments::value_type& value) { values.emplace_back(stringToValue(value.second)); }; - std::for_each(match.first, match.second, addValue); + + auto addToValues = [&values](Arguments::value_type& argValue) {values.emplace_back(stringToValue(argValue.second));}; + std::for_each(match.first, match.second, addToValues); arguments.erase(match.first, match.second); + return true; } -void insertShapesBuild(std::unordered_map& shapes, nvinfer1::OptProfileSelector selector, - const std::string& name, const std::vector& dims) +void insertShapesBuild(std::unordered_map& shapes, nvinfer1::OptProfileSelector selector, const std::string& name, const std::vector& dims) { shapes[name][static_cast(selector)] = dims; } -void insertShapesInference( - std::unordered_map>& shapes, const std::string& name, const std::vector& dims) +void insertShapesInference(std::unordered_map>& shapes, const std::string& name, const std::vector& dims) { shapes[name] = dims; } std::string removeSingleQuotationMarks(std::string& str) { - std::vector strList{splitToStringVec(str, '\'')}; - // Remove all the escaped single quotation marks - std::string retVal = ""; - // Do not really care about unterminated sequences - for (size_t i = 0; i < strList.size(); i++) - { - retVal += strList[i]; - } - return retVal; + std::vector strList{splitToStringVec(str, '\'')}; + // Remove all the escaped single quotation marks + std::string retVal = ""; + // Do not really care about unterminated sequences + for (size_t i = 0; i < strList.size(); i++) + { + retVal += strList[i]; + } + return retVal; } -bool getShapesBuild(Arguments& arguments, std::unordered_map& shapes, const char* argument, - nvinfer1::OptProfileSelector selector) +bool getShapesBuild(Arguments& arguments, std::unordered_map& shapes, const char* argument, nvinfer1::OptProfileSelector selector) { std::string list; - bool retVal = checkEraseOption(arguments, argument, list); + bool retVal = getAndDelOption(arguments, argument, list); std::vector shapeList{splitToStringVec(list, ',')}; for (const auto& s : shapeList) { @@ -256,11 +263,10 @@ bool getShapesBuild(Arguments& arguments, std::unordered_map>& shapes, const char* argument) +bool getShapesInference(Arguments& arguments, std::unordered_map>& shapes, const char* argument) { std::string list; - bool retVal = checkEraseOption(arguments, argument, list); + bool retVal = getAndDelOption(arguments, argument, list); std::vector shapeList{splitToStringVec(list, ',')}; for (const auto& s : shapeList) { @@ -272,23 +278,20 @@ bool getShapesInference( return retVal; } -void processShapes( - std::unordered_map& shapes, bool minShapes, bool optShapes, bool maxShapes, bool calib) +void processShapes(std::unordered_map& shapes, bool minShapes, bool optShapes, bool maxShapes, bool calib) { // Only accept optShapes only or all three of minShapes, optShapes, maxShapes - if (((minShapes || maxShapes) && !optShapes) // minShapes only, maxShapes only, both minShapes and maxShapes + if ( ((minShapes || maxShapes) && !optShapes) // minShapes only, maxShapes only, both minShapes and maxShapes || (minShapes && !maxShapes && optShapes) // both minShapes and optShapes || (!minShapes && maxShapes && optShapes)) // both maxShapes and optShapes { if (calib) { - throw std::invalid_argument( - "Must specify only --optShapesCalib or all of --minShapesCalib, --optShapesCalib, --maxShapesCalib"); + throw std::invalid_argument("Must specify only --optShapesCalib or all of --minShapesCalib, --optShapesCalib, --maxShapesCalib"); } else { - throw std::invalid_argument( - "Must specify only --optShapes or all of --minShapes, --optShapes, --maxShapes"); + throw std::invalid_argument("Must specify only --optShapes or all of --minShapes, --optShapes, --maxShapes"); } } @@ -298,12 +301,9 @@ void processShapes( std::unordered_map newShapes; for (auto& s : shapes) { - insertShapesBuild(newShapes, nvinfer1::OptProfileSelector::kMIN, s.first, - s.second[static_cast(nvinfer1::OptProfileSelector::kOPT)]); - insertShapesBuild(newShapes, nvinfer1::OptProfileSelector::kOPT, s.first, - s.second[static_cast(nvinfer1::OptProfileSelector::kOPT)]); - insertShapesBuild(newShapes, nvinfer1::OptProfileSelector::kMAX, s.first, - s.second[static_cast(nvinfer1::OptProfileSelector::kOPT)]); + insertShapesBuild(newShapes, nvinfer1::OptProfileSelector::kMIN, s.first, s.second[static_cast(nvinfer1::OptProfileSelector::kOPT)]); + insertShapesBuild(newShapes, nvinfer1::OptProfileSelector::kOPT, s.first, s.second[static_cast(nvinfer1::OptProfileSelector::kOPT)]); + insertShapesBuild(newShapes, nvinfer1::OptProfileSelector::kMAX, s.first, s.second[static_cast(nvinfer1::OptProfileSelector::kOPT)]); } shapes = newShapes; } @@ -338,8 +338,7 @@ std::ostream& printBatch(std::ostream& os, int maxBatch) return os; } -std::ostream& printTacticSources( - std::ostream& os, nvinfer1::TacticSources enabledSources, nvinfer1::TacticSources disabledSources) +std::ostream& printTacticSources(std::ostream& os, nvinfer1::TacticSources enabledSources, nvinfer1::TacticSources disabledSources) { if (!enabledSources && !disabledSources) { @@ -347,25 +346,20 @@ std::ostream& printTacticSources( } else { - uint32_t cublas = 1U << static_cast(nvinfer1::TacticSource::kCUBLAS); - uint32_t cublasLt = 1U << static_cast(nvinfer1::TacticSource::kCUBLAS_LT); + const auto addSource = [&](uint32_t source, const std::string& name) { + if (enabledSources & source) + { + os << name << " [ON], "; + } + else if (disabledSources & source) + { + os << name << " [OFF], "; + } + }; - if (enabledSources & cublas) - { - os << " +cublas"; - } - if (disabledSources & cublas) - { - os << " -cublas"; - } - if (enabledSources & cublasLt) - { - os << " +cublasLt"; - } - if (disabledSources & cublasLt) - { - os << " -cublasLt"; - } + addSource(1U << static_cast(nvinfer1::TacticSource::kCUBLAS), "cublas"); + addSource(1U << static_cast(nvinfer1::TacticSource::kCUBLAS_LT), "cublasLt"); + addSource(1U << static_cast(nvinfer1::TacticSource::kCUDNN), "cudnn"); } return os; } @@ -383,6 +377,29 @@ std::ostream& printPrecision(std::ostream& os, const BuildOptions& options) } return os; } + +std::ostream& printTimingCache(std::ostream& os, const BuildOptions& options) +{ + switch (options.timingCacheMode) + { + case TimingCacheMode::kGLOBAL: os << "global"; break; + case TimingCacheMode::kLOCAL: os << "local"; break; + case TimingCacheMode::kDISABLE: os << "disable"; break; + } + return os; +} + +std::ostream& printSparsity(std::ostream& os, const BuildOptions& options) +{ + switch (options.sparsity) + { + case SparsityFlag::kDISABLE: os << "Disabled"; break; + case SparsityFlag::kENABLE: os << "Enabled"; break; + case SparsityFlag::kFORCE: os << "Forced"; break; + } + + return os; +} } // namespace Arguments argsToArgumentsMap(int argc, char* argv[]) @@ -406,15 +423,15 @@ Arguments argsToArgumentsMap(int argc, char* argv[]) void BaseModelOptions::parse(Arguments& arguments) { - if (checkEraseOption(arguments, "--onnx", model)) + if (getAndDelOption(arguments, "--onnx", model)) { format = ModelFormat::kONNX; } - else if (checkEraseOption(arguments, "--uff", model)) + else if (getAndDelOption(arguments, "--uff", model)) { format = ModelFormat::kUFF; } - else if (checkEraseOption(arguments, "--model", model)) + else if (getAndDelOption(arguments, "--model", model)) { format = ModelFormat::kCAFFE; } @@ -422,9 +439,9 @@ void BaseModelOptions::parse(Arguments& arguments) void UffInput::parse(Arguments& arguments) { - checkEraseOption(arguments, "--uffNHWC", NHWC); + getAndDelOption(arguments, "--uffNHWC", NHWC); std::vector args; - if (checkEraseRepeatedOption(arguments, "--uffInput", args)) + if (getAndDelRepeatedOption(arguments, "--uffInput", args)) { for (const auto& i : args) { @@ -450,7 +467,7 @@ void ModelOptions::parse(Arguments& arguments) { case ModelFormat::kCAFFE: { - checkEraseOption(arguments, "--deploy", prototxt); + getAndDelOption(arguments, "--deploy", prototxt); break; } case ModelFormat::kUFF: @@ -462,10 +479,11 @@ void ModelOptions::parse(Arguments& arguments) } break; } - case ModelFormat::kONNX: break; + case ModelFormat::kONNX: + break; case ModelFormat::kANY: { - if (checkEraseOption(arguments, "--deploy", prototxt)) + if (getAndDelOption(arguments, "--deploy", prototxt)) { baseModel.format = ModelFormat::kCAFFE; } @@ -475,7 +493,7 @@ void ModelOptions::parse(Arguments& arguments) if (baseModel.format == ModelFormat::kCAFFE || baseModel.format == ModelFormat::kUFF) { std::vector outArgs; - if (checkEraseRepeatedOption(arguments, "--output", outArgs)) + if (getAndDelRepeatedOption(arguments, "--output", outArgs)) { for (const auto& o : outArgs) { @@ -496,8 +514,8 @@ void BuildOptions::parse(Arguments& arguments) { auto getFormats = [&arguments](std::vector& formatsVector, const char* argument) { std::string list; - checkEraseOption(arguments, argument, list); - const std::vector formats{splitToStringVec(list, ',')}; + getAndDelOption(arguments, argument, list); + std::vector formats{splitToStringVec(list, ',')}; for (const auto& f : formats) { formatsVector.push_back(stringToValue(f)); @@ -508,7 +526,7 @@ void BuildOptions::parse(Arguments& arguments) getFormats(outputFormats, "--outputIOFormats"); bool explicitBatch{false}; - checkEraseOption(arguments, "--explicitBatch", explicitBatch); + getAndDelOption(arguments, "--explicitBatch", explicitBatch); bool minShapes = getShapesBuild(arguments, shapes, "--minShapes", nvinfer1::OptProfileSelector::kMIN); bool optShapes = getShapesBuild(arguments, shapes, "--optShapes", nvinfer1::OptProfileSelector::kOPT); bool maxShapes = getShapesBuild(arguments, shapes, "--maxShapes", nvinfer1::OptProfileSelector::kMAX); @@ -522,8 +540,10 @@ void BuildOptions::parse(Arguments& arguments) processShapes(shapesCalib, minShapesCalib, optShapesCalib, maxShapesCalib, true); explicitBatch = explicitBatch || !shapes.empty(); + getAndDelOption(arguments, "--explicitPrecision", explicitPrecision); + int batch{0}; - checkEraseOption(arguments, "--maxBatch", batch); + getAndDelOption(arguments, "--maxBatch", batch); if (explicitBatch && batch) { throw std::invalid_argument( @@ -542,32 +562,51 @@ void BuildOptions::parse(Arguments& arguments) } } - checkEraseOption(arguments, "--workspace", workspace); - checkEraseOption(arguments, "--minTiming", minTiming); - checkEraseOption(arguments, "--avgTiming", avgTiming); + getAndDelOption(arguments, "--workspace", workspace); + getAndDelOption(arguments, "--minTiming", minTiming); + getAndDelOption(arguments, "--avgTiming", avgTiming); bool best{false}; - checkEraseOption(arguments, "--best", best); + getAndDelOption(arguments, "--best", best); if (best) { int8 = true; fp16 = true; } - checkEraseOption(arguments, "--refit", refittable); - checkEraseNegativeOption(arguments, "--noTF32", tf32); - checkEraseOption(arguments, "--fp16", fp16); - checkEraseOption(arguments, "--int8", int8); - checkEraseOption(arguments, "--safe", safe); - bool calibCheck = checkEraseOption(arguments, "--calib", calibration); + getAndDelOption(arguments, "--refit", refittable); + getAndDelNegOption(arguments, "--noTF32", tf32); + getAndDelOption(arguments, "--fp16", fp16); + getAndDelOption(arguments, "--int8", int8); + getAndDelOption(arguments, "--safe", safe); + + std::string sparsityString; + getAndDelOption(arguments, "--sparsity", sparsityString); + if (sparsityString == "disable") + { + sparsity = SparsityFlag::kDISABLE; + } + else if (sparsityString == "enable") + { + sparsity = SparsityFlag::kENABLE; + } + else if (sparsityString == "force") + { + sparsity = SparsityFlag::kFORCE; + } + else if (!sparsityString.empty()) + { + throw std::invalid_argument(std::string("Unknown sparsity mode: ") + sparsityString); + } + + bool calibCheck = getAndDelOption(arguments, "--calib", calibration); if (int8 && calibCheck && !shapes.empty() && shapesCalib.empty()) { shapesCalib = shapes; } - checkEraseNegativeOption(arguments, "--noBuilderCache", builderCache); std::string nvtxModeString; - checkEraseOption(arguments, "--nvtxMode", nvtxModeString); + getAndDelOption(arguments, "--nvtxMode", nvtxModeString); if (nvtxModeString == "default") { nvtxMode = nvinfer1::ProfilingVerbosity::kDEFAULT; @@ -585,11 +624,11 @@ void BuildOptions::parse(Arguments& arguments) throw std::invalid_argument(std::string("Unknown nvtxMode: ") + nvtxModeString); } - if (checkEraseOption(arguments, "--loadEngine", engine)) + if (getAndDelOption(arguments, "--loadEngine", engine)) { load = true; } - if (checkEraseOption(arguments, "--saveEngine", engine)) + if (getAndDelOption(arguments, "--saveEngine", engine)) { save = true; } @@ -599,7 +638,7 @@ void BuildOptions::parse(Arguments& arguments) } std::string tacticSourceArgs; - if (checkEraseOption(arguments, "--tacticSources", tacticSourceArgs)) + if (getAndDelOption(arguments, "--tacticSources", tacticSourceArgs)) { std::vector tacticList = splitToStringVec(tacticSourceArgs, ','); for (auto& t : tacticList) @@ -611,19 +650,32 @@ void BuildOptions::parse(Arguments& arguments) } else if (t.front() != '-') { - throw std::invalid_argument("Tactic conditional (+|-) is missing"); + throw std::invalid_argument( + "Tactic source must be prefixed with + or -, indicating whether it should be enabled or disabled " + "respectively."); } t.erase(0, 1); + const auto toUpper = [](std::string& sourceName) { + std::transform( + sourceName.begin(), sourceName.end(), sourceName.begin(), [](char c) { return std::toupper(c); }); + return sourceName; + }; + nvinfer1::TacticSource source{}; - if (t == "cublas") + t = toUpper(t); + if (t == "CUBLAS") { source = nvinfer1::TacticSource::kCUBLAS; } - else if (t == "cublasLt") + else if (t == "CUBLASLT" || t == "CUBLAS_LT") { source = nvinfer1::TacticSource::kCUBLAS_LT; } + else if (t == "CUDNN") + { + source = nvinfer1::TacticSource::kCUDNN; + } else { throw std::invalid_argument(std::string("Unknown tactic source: ") + t); @@ -646,15 +698,31 @@ void BuildOptions::parse(Arguments& arguments) } } } + + bool noBuilderCache{false}; + getAndDelOption(arguments, "--noBuilderCache", noBuilderCache); + getAndDelOption(arguments, "--timingCacheFile", timingCacheFile); + if (noBuilderCache) + { + timingCacheMode = TimingCacheMode::kDISABLE; + } + else if (!timingCacheFile.empty()) + { + timingCacheMode = TimingCacheMode::kGLOBAL; + } + else + { + timingCacheMode = TimingCacheMode::kLOCAL; + } } void SystemOptions::parse(Arguments& arguments) { - checkEraseOption(arguments, "--device", device); - checkEraseOption(arguments, "--useDLACore", DLACore); - checkEraseOption(arguments, "--allowGPUFallback", fallback); + getAndDelOption(arguments, "--device", device); + getAndDelOption(arguments, "--useDLACore", DLACore); + getAndDelOption(arguments, "--allowGPUFallback", fallback); std::string pluginName; - while (checkEraseOption(arguments, "--plugins", pluginName)) + while (getAndDelOption(arguments, "--plugins", pluginName)) { plugins.emplace_back(pluginName); } @@ -662,32 +730,34 @@ void SystemOptions::parse(Arguments& arguments) void InferenceOptions::parse(Arguments& arguments) { - checkEraseOption(arguments, "--streams", streams); - checkEraseOption(arguments, "--iterations", iterations); - checkEraseOption(arguments, "--duration", duration); - checkEraseOption(arguments, "--warmUp", warmup); - checkEraseOption(arguments, "--sleepTime", sleep); + getAndDelOption(arguments, "--streams", streams); + getAndDelOption(arguments, "--iterations", iterations); + getAndDelOption(arguments, "--duration", duration); + getAndDelOption(arguments, "--warmUp", warmup); + getAndDelOption(arguments, "--sleepTime", sleep); bool exposeDMA{false}; - if (checkEraseOption(arguments, "--exposeDMA", exposeDMA)) + if (getAndDelOption(arguments, "--exposeDMA", exposeDMA)) { overlap = !exposeDMA; } - checkEraseOption(arguments, "--noDataTransfers", skipTransfers); - checkEraseOption(arguments, "--useSpinWait", spin); - checkEraseOption(arguments, "--threads", threads); - checkEraseOption(arguments, "--useCudaGraph", graph); - checkEraseOption(arguments, "--separateProfileRun", rerun); - checkEraseOption(arguments, "--buildOnly", skip); + getAndDelOption(arguments, "--noDataTransfers", skipTransfers); + getAndDelOption(arguments, "--useSpinWait", spin); + getAndDelOption(arguments, "--threads", threads); + getAndDelOption(arguments, "--useCudaGraph", graph); + getAndDelOption(arguments, "--separateProfileRun", rerun); + getAndDelOption(arguments, "--buildOnly", skip); + getAndDelOption(arguments, "--timeDeserialize", timeDeserialize); + getAndDelOption(arguments, "--timeRefit", timeRefit); std::string list; - checkEraseOption(arguments, "--loadInputs", list); + getAndDelOption(arguments, "--loadInputs", list); std::vector inputsList{splitToStringVec(list, ',')}; splitInsertKeyValue(inputsList, inputs); getShapesInference(arguments, shapes, "--shapes"); int batchOpt{0}; - checkEraseOption(arguments, "--batch", batchOpt); + getAndDelOption(arguments, "--batch", batchOpt); if (!shapes.empty() && batchOpt) { throw std::invalid_argument( @@ -708,15 +778,15 @@ void InferenceOptions::parse(Arguments& arguments) void ReportingOptions::parse(Arguments& arguments) { - checkEraseOption(arguments, "--percentile", percentile); - checkEraseOption(arguments, "--avgRuns", avgs); - checkEraseOption(arguments, "--verbose", verbose); - checkEraseOption(arguments, "--dumpRefit", refit); - checkEraseOption(arguments, "--dumpOutput", output); - checkEraseOption(arguments, "--dumpProfile", profile); - checkEraseOption(arguments, "--exportTimes", exportTimes); - checkEraseOption(arguments, "--exportOutput", exportOutput); - checkEraseOption(arguments, "--exportProfile", exportProfile); + getAndDelOption(arguments, "--percentile", percentile); + getAndDelOption(arguments, "--avgRuns", avgs); + getAndDelOption(arguments, "--verbose", verbose); + getAndDelOption(arguments, "--dumpRefit", refit); + getAndDelOption(arguments, "--dumpOutput", output); + getAndDelOption(arguments, "--dumpProfile", profile); + getAndDelOption(arguments, "--exportTimes", exportTimes); + getAndDelOption(arguments, "--exportOutput", exportOutput); + getAndDelOption(arguments, "--exportProfile", exportProfile); if (percentile < 0 || percentile > 100) { throw std::invalid_argument(std::string("Percentile ") + std::to_string(percentile) + "is not in [0,100]"); @@ -727,8 +797,8 @@ bool parseHelp(Arguments& arguments) { bool helpLong{false}; bool helpShort{false}; - checkEraseOption(arguments, "--help", helpLong); - checkEraseOption(arguments, "-h", helpShort); + getAndDelOption(arguments, "--help", helpLong); + getAndDelOption(arguments, "-h", helpShort); return helpLong || helpShort; } @@ -744,11 +814,19 @@ void AllOptions::parse(Arguments& arguments) build.maxBatch = 0; // ONNX only supports explicit batch mode. } - if ((!build.maxBatch && inference.batch && inference.batch != defaultBatch && !build.shapes.empty()) - || (build.maxBatch && build.maxBatch != defaultMaxBatch && !inference.batch)) + auto batchWasSet = [](int batch, int defaultValue) { return batch && batch != defaultValue; }; + + if (!build.maxBatch && batchWasSet(inference.batch, defaultBatch) && !build.shapes.empty()) { - // If either has selected implict batch and the other has selected explicit batch - throw std::invalid_argument("Conflicting build and inference batch settings"); + throw std::invalid_argument( + "Explicit batch + dynamic shapes setting used at build time but inference uses --batch to set batch. " + "Conflicting build and inference batch settings."); + } + if (batchWasSet(build.maxBatch, defaultMaxBatch) && !inference.batch) + { + throw std::invalid_argument( + "Implicit batch option used at build time but inference input shapes specified. Conflicting build and " + "inference batch settings."); } if (build.shapes.empty() && !inference.shapes.empty()) @@ -834,6 +912,33 @@ void AllOptions::parse(Arguments& arguments) } } +void SafeBuilderOptions::parse(Arguments& arguments) +{ + auto getFormats = [&arguments](std::vector& formatsVector, const char* argument) { + std::string list; + getAndDelOption(arguments, argument, list); + std::vector formats{splitToStringVec(list, ',')}; + for (const auto& f : formats) + { + formatsVector.push_back(stringToValue(f)); + } + }; + + getAndDelOption(arguments, "--serialized", serialized); + getAndDelOption(arguments, "--onnx", onnxModelFile); + getAndDelOption(arguments, "--help", help); + getAndDelOption(arguments, "--verbose", verbose); + getFormats(inputFormats, "--inputIOFormats"); + getFormats(outputFormats, "--outputIOFormats"); + getAndDelOption(arguments, "--int8", int8); + getAndDelOption(arguments, "--calib", calibFile); + std::string pluginName; + while (getAndDelOption(arguments, "--plugins", pluginName)) + { + plugins.emplace_back(pluginName); + } +} + std::ostream& operator<<(std::ostream& os, const BaseModelOptions& options) { os << "=== Model Options ===" << std::endl; @@ -856,7 +961,9 @@ std::ostream& operator<<(std::ostream& os, const BaseModelOptions& options) os << "UFF"; break; } - case ModelFormat::kANY: os << "*"; break; + case ModelFormat::kANY: + os << "*"; + break; } os << std::endl << "Model: " << options.model << std::endl; @@ -890,7 +997,8 @@ std::ostream& operator<<(std::ostream& os, const ModelOptions& options) break; } case ModelFormat::kONNX: // Fallthrough: No options to report for ONNX or the generic case - case ModelFormat::kANY: break; + case ModelFormat::kANY: + break; } os << "Output:"; @@ -959,6 +1067,11 @@ std::ostream& operator<<(std::ostream& os, const IOFormat& format) os << "hwc8"; break; } + case nvinfer1::TensorFormat::kHWC16: + { + os << "hwc16"; + break; + } case nvinfer1::TensorFormat::kCHW4: { os << "chw4"; @@ -1003,7 +1116,7 @@ std::ostream& operator<<(std::ostream& os, const IOFormat& format) } } return os; -}; +} std::ostream& operator<<(std::ostream& os, const ShapeRange& dims) { @@ -1022,7 +1135,7 @@ std::ostream& operator<<(std::ostream& os, const ShapeRange& dims) std::ostream& operator<<(std::ostream& os, const BuildOptions& options) { - // clang-format off +// clang-format off os << "=== Build Options ===" << std::endl << "Max batch: "; printBatch(os, options.maxBatch) << std::endl << @@ -1032,12 +1145,14 @@ std::ostream& operator<<(std::ostream& os, const BuildOptions& options) "Precision: "; printPrecision(os, options) << std::endl << "Calibration: " << (options.int8 && options.calibration.empty() ? "Dynamic" : options.calibration.c_str()) << std::endl << "Refit: " << boolToEnabled(options.refittable) << std::endl << + "Sparsity: "; printSparsity(os, options) << std::endl << "Safe mode: " << boolToEnabled(options.safe) << std::endl << "Save engine: " << (options.save ? options.engine : "") << std::endl << "Load engine: " << (options.load ? options.engine : "") << std::endl << - "Builder Cache: " << boolToEnabled(options.builderCache) << std::endl << "NVTX verbosity: " << static_cast(options.nvtxMode) << std::endl << - "Tactic sources: "; printTacticSources(os, options.enabledTactics, options.disabledTactics) << std::endl; + "Tactic sources: "; printTacticSources(os, options.enabledTactics, options.disabledTactics) << std::endl << + "timingCacheMode: "; printTimingCache(os, options) << std::endl << + "timingCacheFile: "<< options.timingCacheFile << std::endl; // clang-format on auto printIOFormats = [](std::ostream& os, const char* direction, const std::vector formats) { @@ -1047,7 +1162,7 @@ std::ostream& operator<<(std::ostream& os, const BuildOptions& options) } else { - for (const auto& f : formats) + for(const auto& f : formats) { os << direction << ": " << f << std::endl; } @@ -1070,7 +1185,6 @@ std::ostream& operator<<(std::ostream& os, const SystemOptions& options) "Device: " << options.device << std::endl << "DLACore: " << (options.DLACore != -1 ? std::to_string(options.DLACore) : "") << (options.DLACore != -1 && options.fallback ? "(With GPU fallback)" : "") << std::endl; - // clang-format on os << "Plugins:"; for (const auto& p : options.plugins) @@ -1080,37 +1194,40 @@ std::ostream& operator<<(std::ostream& os, const SystemOptions& options) os << std::endl; return os; + // clang-format on } std::ostream& operator<<(std::ostream& os, const InferenceOptions& options) { - // clang-format off - os << "=== Inference Options ===" << std::endl << +// clang-format off + os << "=== Inference Options ===" << std::endl << "Batch: "; if (options.batch && options.shapes.empty()) { - os << options.batch << std::endl; + os << options.batch << std::endl; } else { - os << "Explicit" << std::endl; + os << "Explicit" << std::endl; } printShapes(os, "inference", options.shapes); - os << "Iterations: " << options.iterations << std::endl << - "Duration: " << options.duration << "s (+ " - << options.warmup << "ms warm up)" << std::endl << - "Sleep time: " << options.sleep << "ms" << std::endl << - "Streams: " << options.streams << std::endl << - "ExposeDMA: " << boolToEnabled(!options.overlap) << std::endl << - "Data transfers: " << boolToEnabled(!options.skipTransfers) << std::endl << - "Spin-wait: " << boolToEnabled(options.spin) << std::endl << - "Multithreading: " << boolToEnabled(options.threads) << std::endl << - "CUDA Graph: " << boolToEnabled(options.graph) << std::endl << - "Separate profiling: " << boolToEnabled(options.rerun) << std::endl << - "Skip inference: " << boolToEnabled(options.skip) << std::endl; + os << "Iterations: " << options.iterations << std::endl << + "Duration: " << options.duration << "s (+ " + << options.warmup << "ms warm up)" << std::endl << + "Sleep time: " << options.sleep << "ms" << std::endl << + "Streams: " << options.streams << std::endl << + "ExposeDMA: " << boolToEnabled(!options.overlap) << std::endl << + "Data transfers: " << boolToEnabled(!options.skipTransfers) << std::endl << + "Spin-wait: " << boolToEnabled(options.spin) << std::endl << + "Multithreading: " << boolToEnabled(options.threads) << std::endl << + "CUDA Graph: " << boolToEnabled(options.graph) << std::endl << + "Separate profiling: " << boolToEnabled(options.rerun) << std::endl << + "Time Deserialize: " << boolToEnabled(options.timeDeserialize) << std::endl << + "Time Refit: " << boolToEnabled(options.timeRefit) << std::endl << + "Skip inference: " << boolToEnabled(options.skip) << std::endl; - // clang-format on +// clang-format on os << "Inputs:" << std::endl; for (const auto& input : options.inputs) { @@ -1122,19 +1239,19 @@ std::ostream& operator<<(std::ostream& os, const InferenceOptions& options) std::ostream& operator<<(std::ostream& os, const ReportingOptions& options) { - // clang-format off - os << "=== Reporting Options ===" << std::endl << +// clang-format off + os << "=== Reporting Options ===" << std::endl << - "Verbose: " << boolToEnabled(options.verbose) << std::endl << - "Averages: " << options.avgs << " inferences" << std::endl << - "Percentile: " << options.percentile << std::endl << - "Dump refittable layers:" << boolToEnabled(options.refit) << std::endl << - "Dump output: " << boolToEnabled(options.output) << std::endl << - "Profile: " << boolToEnabled(options.profile) << std::endl << - "Export timing to JSON file: " << options.exportTimes << std::endl << - "Export output to JSON file: " << options.exportOutput << std::endl << - "Export profile to JSON file: " << options.exportProfile << std::endl; - // clang-format on + "Verbose: " << boolToEnabled(options.verbose) << std::endl << + "Averages: " << options.avgs << " inferences" << std::endl << + "Percentile: " << options.percentile << std::endl << + "Dump refittable layers:" << boolToEnabled(options.refit) << std::endl << + "Dump output: " << boolToEnabled(options.output) << std::endl << + "Profile: " << boolToEnabled(options.profile) << std::endl << + "Export timing to JSON file: " << options.exportTimes << std::endl << + "Export output to JSON file: " << options.exportOutput << std::endl << + "Export profile to JSON file: " << options.exportProfile << std::endl; +// clang-format on return os; } @@ -1145,40 +1262,80 @@ std::ostream& operator<<(std::ostream& os, const AllOptions& options) return os; } +std::ostream& operator<<(std::ostream& os, const SafeBuilderOptions& options) +{ + auto printIOFormats = [](std::ostream& os, const char* direction, const std::vector formats) { + if (formats.empty()) + { + os << direction << "s format: fp32:CHW" << std::endl; + } + else + { + for(const auto& f : formats) + { + os << direction << ": " << f << std::endl; + } + } + }; + + os << "=== Build Options ===" << std::endl; + os << "Model ONNX: " << options.onnxModelFile << std::endl; + + os << "Precision: FP16"; + if (options.int8) + { + os << " + INT8"; + } + os << std::endl; + os << "Calibration file: " << options.calibFile << std::endl; + os << "Serialized Network: " << options.serialized << std::endl; + + printIOFormats(os, "Input(s)", options.inputFormats); + printIOFormats(os, "Output(s)", options.outputFormats); + + os << "Plugins:"; + for (const auto& p : options.plugins) + { + os << " " << p; + } + os << std::endl; + return os; +} + void BaseModelOptions::help(std::ostream& os) { - // clang-format off +// clang-format off os << " --uff= UFF model" << std::endl << " --onnx= ONNX model" << std::endl << " --model= Caffe model (default = no model, random weights used)" << std::endl; - // clang-format on +// clang-format on } void UffInput::help(std::ostream& os) { - // clang-format off +// clang-format off os << " --uffInput=,X,Y,Z Input blob name and its dimensions (X,Y,Z=C,H,W), it can be specified " "multiple times; at least one is required for UFF models" << std::endl << " --uffNHWC Set if inputs are in the NHWC layout instead of NCHW (use " << "X,Y,Z=H,W,C order in --uffInput)" << std::endl; - // clang-format on +// clang-format on } void ModelOptions::help(std::ostream& os) { - // clang-format off +// clang-format off os << "=== Model Options ===" << std::endl; BaseModelOptions::help(os); os << " --deploy= Caffe prototxt file" << std::endl << " --output=[,]* Output names (it can be specified multiple times); at least one output " "is required for UFF and Caffe" << std::endl; UffInput::help(os); - // clang-format on +// clang-format on } void BuildOptions::help(std::ostream& os) { - // clang-format off +// clang-format off os << "=== Build Options ===" << std::endl << " --maxBatch Set max batch size and build an implicit batch engine (default = " << defaultMaxBatch << ")" << std::endl << @@ -1213,15 +1370,22 @@ void BuildOptions::help(std::ostream& os) " type ::= \"fp32\"|\"fp16\"|\"int32\"|\"int8\"" << std::endl << " fmt ::= (\"chw\"|\"chw2\"|\"chw4\"|\"hwc8\"|\"chw16\"|\"chw32\"|\"dhwc8\")[\"+\"fmt]" << std::endl << " --workspace=N Set workspace size in megabytes (default = " << defaultWorkspace << ")" << std::endl << - " --noBuilderCache Disable timing cache in builder (default is to enable timing cache)" << std::endl << " --nvtxMode=mode Specify NVTX annotation verbosity. mode ::= default|verbose|none" << std::endl << " --minTiming=M Set the minimum number of iterations used in kernel selection (default = " << defaultMinTiming << ")" << std::endl << " --avgTiming=M Set the number of times averaged in each iteration for kernel selection (default = " << defaultAvgTiming << ")" << std::endl << - " --noTF32 Disable tf32 precision (default is to enable tf32, in addition to fp32)" << std::endl << " --refit Mark the engine as refittable. This will allow the inspection of refittable layers " << std::endl << " and weights within the engine." << std::endl << + " --sparsity=spec Control sparsity (default = disabled). " << std::endl << + " Sparsity: spec ::= \"disable\", \"enable\", \"force\"" << std::endl << + " Note: Description about each of these options is as below" << std::endl << + " disable = do not enable sparse tactics in the builder (this is the default)" << std::endl << + " enable = enable sparse tactics in the builder (but these tactics will only be" << std::endl << + " considered if the weights have the right sparsity pattern)" << std::endl << + " force = enable sparse tactics in the builder and force-overwrite the weights to have" << std::endl << + " a sparsity pattern (even if you loaded a model yourself)" << std::endl << + " --noTF32 Disable tf32 precision (default is to enable tf32, in addition to fp32)" << std::endl << " --fp16 Enable fp16 precision, in addition to fp32 (default = disabled)" << std::endl << " --int8 Enable int8 precision, in addition to fp32 (default = disabled)" << std::endl << " --best Enable all precisions to achieve the best performance (default = disabled)" << std::endl << @@ -1231,65 +1395,72 @@ void BuildOptions::help(std::ostream& os) " --loadEngine= Load a serialized engine" << std::endl << " --tacticSources=tactics Specify the tactics to be used by adding (+) or removing (-) tactics from the default " << std::endl << " tactic sources (default = all available tactics)." << std::endl << - " Note: Currently only cuBLAS and cuBLAS LT are listed as optional tactics." << std::endl << + " Note: Currently only cuDNN, cuBLAS and cuBLAS-LT are listed as optional tactics." << std::endl << " Tactic Sources: tactics ::= [\",\"tactic]" << std::endl << " tactic ::= (+|-)lib" << std::endl << - " lib ::= \"cublas\"|\"cublasLt\"" << std::endl; - // clang-format on + " lib ::= \"CUBLAS\"|\"CUBLAS_LT\"|\"CUDNN\"" << std::endl << + " For example, to disable cudnn and enable cublas: --tacticSources=-CUDNN,+CUBLAS" << std::endl << + " --noBuilderCache Disable timing cache in builder (default is to enable timing cache)" << std::endl << + " --timingCacheFile= Save/load the serialized global timing cache" << std::endl + ; +// clang-format on } void SystemOptions::help(std::ostream& os) { - // clang-format off +// clang-format off os << "=== System Options ===" << std::endl << " --device=N Select cuda device N (default = " << defaultDevice << ")" << std::endl << " --useDLACore=N Select DLA core N for layers that support DLA (default = none)" << std::endl << " --allowGPUFallback When DLA is enabled, allow GPU fallback for unsupported layers " "(default = disabled)" << std::endl; os << " --plugins Plugin library (.so) to load (can be specified multiple times)" << std::endl; - // clang-format on +// clang-format on } void InferenceOptions::help(std::ostream& os) { // clang-format off - os << "=== Inference Options ===" << std::endl << - " --batch=N Set batch size for implicit batch engines (default = " << defaultBatch << ")" << std::endl << - " --shapes=spec Set input shapes for dynamic shapes inference inputs." << std::endl << - " Note: Use of dynamic shapes implies explicit batch." << std::endl << - " Input names can be wrapped with escaped single quotes (ex: \\\'Input:0\\\')." << std::endl << - " Example input shapes spec: input0:1x3x256x256, input1:1x3x128x128" << std::endl << - " Each input shape is supplied as a key-value pair where key is the input name and" << std::endl << - " value is the dimensions (including the batch dimension) to be used for that input." << std::endl << - " Each key-value pair has the key and value separated using a colon (:)." << std::endl << - " Multiple input shapes can be provided via comma-separated key-value pairs." << std::endl << + os << "=== Inference Options ===" << std::endl << + " --batch=N Set batch size for implicit batch engines (default = " << defaultBatch << ")" << std::endl << + " --shapes=spec Set input shapes for dynamic shapes inference inputs." << std::endl << + " Note: Use of dynamic shapes implies explicit batch." << std::endl << + " Input names can be wrapped with escaped single quotes (ex: \\\'Input:0\\\')." << std::endl << + " Example input shapes spec: input0:1x3x256x256, input1:1x3x128x128" << std::endl << + " Each input shape is supplied as a key-value pair where key is the input name and" << std::endl << + " value is the dimensions (including the batch dimension) to be used for that input." << std::endl << + " Each key-value pair has the key and value separated using a colon (:)." << std::endl << + " Multiple input shapes can be provided via comma-separated key-value pairs." << std::endl << " --loadInputs=spec Load input values from files (default = generate random inputs). Input names can be " - "wrapped with single quotes (ex: 'Input:0')" << std::endl << - " Input values spec ::= Ival[\",\"spec]" << std::endl << - " Ival ::= name\":\"file" << std::endl << - " --iterations=N Run at least N inference iterations (default = " << defaultIterations << ")" << std::endl << + "wrapped with single quotes (ex: 'Input:0')" << std::endl << + " Input values spec ::= Ival[\",\"spec]" << std::endl << + " Ival ::= name\":\"file" << std::endl << + " --iterations=N Run at least N inference iterations (default = " << defaultIterations << ")" << std::endl << " --warmUp=N Run for N milliseconds to warmup before measuring performance (default = " - << defaultWarmUp << ")" << std::endl << + << defaultWarmUp << ")" << std::endl << " --duration=N Run performance measurements for at least N seconds wallclock time (default = " - << defaultDuration << ")" << std::endl << + << defaultDuration << ")" << std::endl << " --sleepTime=N Delay inference start with a gap of N milliseconds between launch and compute " - "(default = " << defaultSleep << ")" << std::endl << - " --streams=N Instantiate N engines to use concurrently (default = " << defaultStreams << ")" << std::endl << - " --exposeDMA Serialize DMA transfers to and from device. (default = disabled)" << std::endl << - " --noDataTransfers Do not transfer data to and from the device during inference. (default = disabled)" << std::endl << + "(default = " << defaultSleep << ")" << std::endl << + " --streams=N Instantiate N engines to use concurrently (default = " << defaultStreams << ")" << std::endl << + " --exposeDMA Serialize DMA transfers to and from device (default = disabled)." << std::endl << + " --noDataTransfers Disable DMA transfers to and from device (default = enabled)." << std::endl << " --useSpinWait Actively synchronize on GPU events. This option may decrease synchronization time but " - "increase CPU usage and power (default = disabled)" << std::endl << - " --threads Enable multithreading to drive engines with independent threads (default = disabled)" << std::endl << - " --useCudaGraph Use cuda graph to capture engine execution and then launch inference (default = disabled)" << std::endl << + "increase CPU usage and power (default = disabled)" << std::endl << + " --threads Enable multithreading to drive engines with independent threads (default = disabled)" << std::endl << + " --useCudaGraph Use CUDA graph to capture engine execution and then launch inference (default = disabled)." << std::endl << + " This flag may be ignored if the graph capture fails." << std::endl << + " --timeDeserialize Time the amount of time it takes to deserialize the network and exit." << std::endl << + " --timeRefit Time the amount of time it takes to refit the engine before inference." << std::endl << " --separateProfileRun Do not attach the profiler in the benchmark run; if profiling is enabled, a second " - "profile run will be executed (default = disabled)" << std::endl << - " --buildOnly Skip inference perf measurement (default = disabled)" << std::endl; + "profile run will be executed (default = disabled)" << std::endl << + " --buildOnly Skip inference perf measurement (default = disabled)" << std::endl; // clang-format on } void ReportingOptions::help(std::ostream& os) { - // clang-format off +// clang-format off os << "=== Reporting Options ===" << std::endl << " --verbose Use verbose logging (default = false)" << std::endl << " --avgRuns=N Report performance measurements averaged over N consecutive " @@ -1306,15 +1477,15 @@ void ReportingOptions::help(std::ostream& os) " --exportOutput= Write the output tensors to a json file (default = disabled)" << std::endl << " --exportProfile= Write the profile information per layer in a json file " "(default = disabled)" << std::endl; - // clang-format on +// clang-format on } void helpHelp(std::ostream& os) { - // clang-format off +// clang-format off os << "=== Help ===" << std::endl << " --help, -h Print this message" << std::endl; - // clang-format on +// clang-format on } void AllOptions::help(std::ostream& os) @@ -1325,7 +1496,7 @@ void AllOptions::help(std::ostream& os) os << std::endl; InferenceOptions::help(os); os << std::endl; - // clang-format off +// clang-format off os << "=== Build and Inference Batch Options ===" << std::endl << " When using implicit batch, the max batch size of the engine, if not given, " << std::endl << " is set to the inference batch size;" << std::endl << @@ -1335,6 +1506,7 @@ void AllOptions::help(std::ostream& os) " if both are specified, they must be compatible; and if explicit batch is " << std::endl << " enabled but neither is specified, the model must provide complete static" << std::endl << " dimensions, including batch size, for all inputs" << std::endl << + " Using ONNX models automatically forces explicit batch." << std::endl << std::endl; // clang-format on ReportingOptions::help(os); @@ -1344,4 +1516,34 @@ void AllOptions::help(std::ostream& os) helpHelp(os); } +void SafeBuilderOptions::printHelp(std::ostream& os) +{ +// clang-format off + os << "=== Mandatory ===" << std::endl << + " --onnx= ONNX model" << std::endl << + " " << std::endl << + "=== Optional ===" << std::endl << + " --inputIOFormats=spec Type and format of each of the input tensors (default = all inputs in fp32:chw)" << std::endl << + " See --outputIOFormats help for the grammar of type and format list." << std::endl << + " Note: If this option is specified, please set comma-separated types and formats for all" << std::endl << + " inputs following the same order as network inputs ID (even if only one input" << std::endl << + " needs specifying IO format) or set the type and format once for broadcasting." << std::endl << + " --outputIOFormats=spec Type and format of each of the output tensors (default = all outputs in fp32:chw)" << std::endl << + " Note: If this option is specified, please set comma-separated types and formats for all" << std::endl << + " outputs following the same order as network outputs ID (even if only one output" << std::endl << + " needs specifying IO format) or set the type and format once for broadcasting." << std::endl << + " IO Formats: spec ::= IOfmt[\",\"spec]" << std::endl << + " IOfmt ::= type:fmt" << std::endl << + " type ::= \"fp32\"|\"fp16\"|\"int32\"|\"int8\"" << std::endl << + " fmt ::= (\"chw\"|\"chw2\"|\"chw4\"|\"hwc8\"|\"chw16\"|\"chw32\"|\"dhwc8\")[\"+\"fmt]" << std::endl << + " --int8 Enable int8 precision, in addition to fp16 (default = disabled)" << std::endl << + " --calib= Read INT8 calibration cache file" << std::endl << + " --serialized= Save the serialized network" << std::endl << + " --plugins Plugin library (.so) to load (can be specified multiple times)" << std::endl << + " --verbose Use verbose logging (default = false)" << std::endl << + " --help Print this message" << std::endl << + " " << std::endl; +// clang-format on +} + } // namespace sample diff --git a/samples/common/sampleOptions.h b/samples/common/sampleOptions.h index 68c30e37..d222298b 100644 --- a/samples/common/sampleOptions.h +++ b/samples/common/sampleOptions.h @@ -60,6 +60,20 @@ enum class ModelFormat kUFF }; +enum class SparsityFlag +{ + kDISABLE, + kENABLE, + kFORCE +}; + +enum class TimingCacheMode +{ + kDISABLE, + kLOCAL, + kGLOBAL +}; + using Arguments = std::unordered_multimap; using IOFormat = std::pair; @@ -110,13 +124,14 @@ struct BuildOptions : public Options int minTiming{defaultMinTiming}; int avgTiming{defaultAvgTiming}; bool tf32{true}; - bool refittable{false}; bool fp16{false}; bool int8{false}; bool safe{false}; bool save{false}; bool load{false}; - bool builderCache{true}; + bool refittable{false}; + bool explicitPrecision{false}; + SparsityFlag sparsity{SparsityFlag::kDISABLE}; nvinfer1::ProfilingVerbosity nvtxMode{nvinfer1::ProfilingVerbosity::kDEFAULT}; std::string engine; std::string calibration; @@ -126,6 +141,8 @@ struct BuildOptions : public Options std::vector outputFormats; nvinfer1::TacticSources enabledTactics{0}; nvinfer1::TacticSources disabledTactics{0}; + TimingCacheMode timingCacheMode{TimingCacheMode::kLOCAL}; + std::string timingCacheFile{}; void parse(Arguments& arguments) override; static void help(std::ostream& out); @@ -158,6 +175,8 @@ struct InferenceOptions : public Options bool graph{false}; bool skip{false}; bool rerun{false}; + bool timeDeserialize{false}; + bool timeRefit{false}; std::unordered_map inputs; std::unordered_map> shapes; @@ -183,6 +202,23 @@ struct ReportingOptions : public Options static void help(std::ostream& out); }; +struct SafeBuilderOptions : public Options +{ + std::string serialized{}; + std::string onnxModelFile{}; + bool help{false}; + bool verbose{false}; + std::vector inputFormats; + std::vector outputFormats; + bool int8{false}; + std::string calibFile{}; + std::vector plugins; + + void parse(Arguments& arguments) override; + + static void printHelp(std::ostream& out); +}; + struct AllOptions : public Options { ModelOptions model; @@ -225,6 +261,8 @@ std::ostream& operator<<(std::ostream& os, const ReportingOptions& options); std::ostream& operator<<(std::ostream& os, const AllOptions& options); +std::ostream& operator<<(std::ostream& os, const SafeBuilderOptions& options); + } // namespace sample #endif // TRT_SAMPLES_OPTIONS_H diff --git a/samples/common/sampleReporting.cpp b/samples/common/sampleReporting.cpp index 4529d2c8..e29cee03 100644 --- a/samples/common/sampleReporting.cpp +++ b/samples/common/sampleReporting.cpp @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -35,17 +36,22 @@ namespace //! //! \brief Find percentile in an ascending sequence of timings +//! \note percentile must be in [0, 100]. Otherwise, an exception is thrown. //! template -float findPercentile(float percentage, const std::vector& timings, const T& toFloat) +float findPercentile(float percentile, const std::vector& timings, const T& toFloat) { const int all = static_cast(timings.size()); - const int exclude = static_cast((1 - percentage / 100) * all); - if (0 <= exclude && exclude <= all) + const int exclude = static_cast((1 - percentile / 100) * all); + if (timings.empty()) { - return toFloat(timings[std::max(all - 1 - exclude, 0)]); + return std::numeric_limits::infinity(); } - return std::numeric_limits::infinity(); + if (percentile < 0.0f || percentile > 100.0f) + { + throw std::runtime_error("percentile is not in [0, 100]!"); + } + return toFloat(timings[std::max(all - 1 - exclude, 0)]); } //! @@ -70,9 +76,9 @@ float findMedian(const std::vector& timings, const T& toFloat) inline InferenceTime traceToTiming(const InferenceTrace& a) { - return InferenceTime((a.enqEnd - a.enqStart), (a.inEnd - a.inStart), (a.computeEnd - a.computeStart), - (a.outEnd - a.outStart), (a.outEnd - a.inStart)); -}; + return InferenceTime((a.enqEnd - a.enqStart), (a.h2dEnd - a.h2dStart), (a.computeEnd - a.computeStart), + (a.d2hEnd - a.d2hStart), (a.d2hEnd - a.h2dStart)); +} } // namespace @@ -87,6 +93,8 @@ void printTiming(const std::vector& timings, int runsPerAvg, std: int count = 0; InferenceTime sum; + os << std::endl; + os << "=== Trace details ===" << std::endl; os << "Trace averages of " << runsPerAvg << " runs:" << std::endl; for (const auto& t : timings) { @@ -94,117 +102,163 @@ void printTiming(const std::vector& timings, int runsPerAvg, std: if (++count == runsPerAvg) { - // clang off + // clang-format off os << "Average on " << runsPerAvg << " runs - GPU latency: " << sum.compute / runsPerAvg << " ms - Host latency: " << sum.latency() / runsPerAvg << " ms (end to end " << sum.e2e / runsPerAvg << " ms, enqueue " << sum.enq / runsPerAvg << " ms)" << std::endl; - // clang on + // clang-format on count = 0; sum.enq = 0; - sum.in = 0; + sum.h2d = 0; sum.compute = 0; - sum.out = 0; + sum.d2h = 0; sum.e2e = 0; } } } -void printEpilog(std::vector timings, float walltimeMs, float percentile, int queries, std::ostream& os) +void printMetricExplanations(std::ostream& os) { - const InferenceTime totalTime = std::accumulate(timings.begin(), timings.end(), InferenceTime()); + os << std::endl; + os << "=== Explanations of the performance metrics ===" << std::endl; + os << "Total Host Walltime: the host walltime from when the first query (after warmups) is enqueued to when the " + "last query is completed." + << std::endl; + os << "GPU Compute Time: the GPU latency to execute the kernels for a query." << std::endl; + os << "Total GPU Compute Time: the summation of the GPU Compute Time of all the queries. If this is significantly " + "shorter than Total Host Walltime, the GPU may be under-utilized because of host-side overheads or data " + "transfers." + << std::endl; + os << "Throughput: the observed throughput computed by dividing the number of queries by the Total Host Walltime. " + "If this is significantly lower than the reciprocal of GPU Compute Time, the GPU may be under-utilized " + "because of host-side overheads or data transfers." + << std::endl; + os << "Enqueue Time: the host latency to enqueue a query. If this is longer than GPU Compute Time, the GPU may be " + "under-utilized." + << std::endl; + os << "H2D Latency: the latency for host-to-device data transfers for input tensors of a single query." + << std::endl; + os << "D2H Latency: the latency for device-to-host data transfers for output tensors of a single query." + << std::endl; + os << "Latency: the summation of H2D Latency, GPU Compute Time, and D2H Latency. This is the latency to infer a " + "single query." + << std::endl; + os << "End-to-End Host Latency: the duration from when the H2D of a query is called to when the D2H of the same " + "query is completed, which includes the latency to wait for the completion of the previous query. This is " + "the latency of a query if multiple queries are enqueued consecutively." + << std::endl; +} + +PerformanceResult getPerformanceResult(const std::vector& timings, + std::function metricGetter, float percentile) +{ + const auto metricComparator + = [metricGetter](const InferenceTime& a, const InferenceTime& b) { return metricGetter(a) < metricGetter(b); }; + const auto metricAccumulator = [metricGetter](float acc, const InferenceTime& a) { return acc + metricGetter(a); }; + std::vector newTimings = timings; + std::sort(newTimings.begin(), newTimings.end(), metricComparator); + PerformanceResult result; + result.min = metricGetter(newTimings.front()); + result.max = metricGetter(newTimings.back()); + result.mean = std::accumulate(newTimings.begin(), newTimings.end(), 0.0f, metricAccumulator) / newTimings.size(); + result.median = findMedian(newTimings, metricGetter); + result.percentile = findPercentile(percentile, newTimings, metricGetter); + return result; +} + +void printEpilog(const std::vector& timings, float walltimeMs, float percentile, int batchSize, + std::ostream& osInfo, std::ostream& osWarning, std::ostream& osVerbose) +{ + const float throughput = batchSize * timings.size() / walltimeMs * 1000; const auto getLatency = [](const InferenceTime& t) { return t.latency(); }; - const auto cmpLatency = [](const InferenceTime& a, const InferenceTime& b) { return a.latency() < b.latency(); }; - std::sort(timings.begin(), timings.end(), cmpLatency); - const float latencyMin = timings.front().latency(); - const float latencyMax = timings.back().latency(); - const float latencyMedian = findMedian(timings, getLatency); - const float latencyPercentile = findPercentile(percentile, timings, getLatency); - const float latencyThroughput = queries * timings.size() / walltimeMs * 1000; + const auto latencyResult = getPerformanceResult(timings, getLatency, percentile); const auto getEndToEnd = [](const InferenceTime& t) { return t.e2e; }; - const auto cmpEndToEnd = [](const InferenceTime& a, const InferenceTime& b) { return a.e2e < b.e2e; }; - std::sort(timings.begin(), timings.end(), cmpEndToEnd); - const float endToEndMin = timings.front().e2e; - const float endToEndMax = timings.back().e2e; - const float endToEndMedian = findMedian(timings, getEndToEnd); - const float endToEndPercentile = findPercentile(percentile, timings, getEndToEnd); - - const auto getCompute = [](const InferenceTime& t) { return t.compute; }; - const auto cmpCompute = [](const InferenceTime& a, const InferenceTime& b) { return a.compute < b.compute; }; - std::sort(timings.begin(), timings.end(), cmpCompute); - const float gpuMin = timings.front().compute; - const float gpuMax = timings.back().compute; - const float gpuMedian = findMedian(timings, getCompute); - const float gpuPercentile = findPercentile(percentile, timings, getCompute); + const auto e2eLatencyResult = getPerformanceResult(timings, getEndToEnd, percentile); const auto getEnqueue = [](const InferenceTime& t) { return t.enq; }; - const auto cmpEnqueue = [](const InferenceTime& a, const InferenceTime& b) { return a.enq < b.enq; }; - std::sort(timings.begin(), timings.end(), cmpEnqueue); - const float enqMin = timings.front().enq; - const float enqMax = timings.back().enq; - const float enqMedian = findMedian(timings, getEnqueue); + const auto enqueueResult = getPerformanceResult(timings, getEnqueue, percentile); - // clang off - os << "Host Latency" << std::endl - << "min: " << latencyMin - << " ms " - "(end to end " - << endToEndMin << " ms)" << std::endl - << "max: " << latencyMax - << " ms " - "(end to end " - << endToEndMax << " ms)" << std::endl - << "mean: " << totalTime.latency() / timings.size() - << " ms " - "(end to end " - << totalTime.e2e / timings.size() << " ms)" << std::endl - << "median: " << latencyMedian - << " ms " - "(end to end " - << endToEndMedian << " ms)" << std::endl - << "percentile: " << latencyPercentile - << " ms " - "at " - << percentile - << "% " - "(end to end " - << endToEndPercentile - << " ms " - "at " - << percentile << "%)" << std::endl - << "throughput: " << latencyThroughput << " qps" << std::endl - << "walltime: " << walltimeMs / 1000 << " s" << std::endl - << "Enqueue Time" << std::endl - << "min: " << enqMin << " ms" << std::endl - << "max: " << enqMax << " ms" << std::endl - << "median: " << enqMedian << " ms" << std::endl - << "GPU Compute" << std::endl - << "min: " << gpuMin << " ms" << std::endl - << "max: " << gpuMax << " ms" << std::endl - << "mean: " << totalTime.compute / timings.size() << " ms" << std::endl - << "median: " << gpuMedian << " ms" << std::endl - << "percentile: " << gpuPercentile - << " ms " - "at " - << percentile << "%" << std::endl - << "total compute time: " << totalTime.compute / 1000 << " s" << std::endl; - // clang on + const auto getH2d = [](const InferenceTime& t) { return t.h2d; }; + const auto h2dResult = getPerformanceResult(timings, getH2d, percentile); + + const auto getCompute = [](const InferenceTime& t) { return t.compute; }; + const auto gpuComputeResult = getPerformanceResult(timings, getCompute, percentile); + + const auto getD2h = [](const InferenceTime& t) { return t.d2h; }; + const auto d2hResult = getPerformanceResult(timings, getD2h, percentile); + + const auto toPerfString = [percentile](const PerformanceResult& r) { + std::stringstream s; + s << "min = " << r.min << " ms, max = " << r.max << " ms, mean = " << r.mean << " ms, " + << "median = " << r.median << " ms, percentile(" << percentile << "%) = " << r.percentile << " ms"; + return s.str(); + }; + + osInfo << std::endl; + osInfo << "=== Performance summary ===" << std::endl; + osInfo << "Throughput: " << throughput << " qps" << std::endl; + osInfo << "Latency: " << toPerfString(latencyResult) << std::endl; + osInfo << "End-to-End Host Latency: " << toPerfString(e2eLatencyResult) << std::endl; + osInfo << "Enqueue Time: " << toPerfString(enqueueResult) << std::endl; + osInfo << "H2D Latency: " << toPerfString(h2dResult) << std::endl; + osInfo << "GPU Compute Time: " << toPerfString(gpuComputeResult) << std::endl; + osInfo << "D2H Latency: " << toPerfString(d2hResult) << std::endl; + osInfo << "Total Host Walltime: " << walltimeMs / 1000 << " s" << std::endl; + osInfo << "Total GPU Compute Time: " << gpuComputeResult.mean * timings.size() / 1000 << " s" << std::endl; + + // Report warnings if the throughput is bound by other factors than GPU + // Compute Time. + constexpr float enqueueBoundReportingThreshold{0.8f}; + if (enqueueResult.median > enqueueBoundReportingThreshold * gpuComputeResult.median) + { + osWarning + << "* Throughput may be bound by Enqueue Time rather than GPU Compute and the GPU may be under-utilized." + << std::endl; + osWarning << " If not already in use, --useCudaGraph (utilize CUDA graphs where possible) may increase the " + "throughput." + << std::endl; + } + if (h2dResult.median >= gpuComputeResult.median) + { + osWarning << "* Throughput may be bound by host-to-device transfers for the inputs rather than GPU Compute and " + "the GPU may be under-utilized." + << std::endl; + osWarning << " Add --noDataTransfers flag to disable data transfers." << std::endl; + } + if (d2hResult.median >= gpuComputeResult.median) + { + osWarning << "* Throughput may be bound by device-to-host transfers for the outputs rather than GPU Compute " + "and the GPU may be under-utilized." + << std::endl; + osWarning << " Add --noDataTransfers flag to disable data transfers." << std::endl; + } + + // Explain what the metrics mean. + osInfo << "Explanations of the performance metrics are printed in the verbose logs." << std::endl; + printMetricExplanations(osVerbose); + + osInfo << std::endl; } void printPerformanceReport(const std::vector& trace, const ReportingOptions& reporting, float warmupMs, - int queries, std::ostream& os) + int batchSize, std::ostream& osInfo, std::ostream& osWarning, std::ostream& osVerbose) { const auto isNotWarmup = [&warmupMs](const InferenceTrace& a) { return a.computeStart >= warmupMs; }; const auto noWarmup = std::find_if(trace.begin(), trace.end(), isNotWarmup); const int warmups = noWarmup - trace.begin(); - const float benchTime = trace.back().outEnd - noWarmup->inStart; - printProlog(warmups * queries, (trace.size() - warmups) * queries, warmupMs, benchTime, os); + const float benchTime = trace.back().d2hEnd - noWarmup->h2dStart; + // when implicit batch used, batchSize = options.inference.batch, which is parsed through --batch + // when explicit batch used, batchSize = options.inference.batch = 0 + // treat inference with explicit batch as a single query and report the throughput + batchSize = batchSize ? batchSize : 1; + printProlog(warmups * batchSize, (trace.size() - warmups) * batchSize, warmupMs, benchTime, osInfo); std::vector timings(trace.size() - warmups); std::transform(noWarmup, trace.end(), timings.begin(), traceToTiming); - printTiming(timings, reporting.avgs, os); - printEpilog(timings, benchTime, reporting.percentile, queries, os); + printTiming(timings, reporting.avgs, osInfo); + printEpilog(timings, benchTime, reporting.percentile, batchSize, osInfo, osWarning, osVerbose); if (!reporting.exportTimes.empty()) { @@ -214,9 +268,9 @@ void printPerformanceReport(const std::vector& trace, const Repo //! Printed format: //! [ value, ...] -//! value ::= { "start enq : time, "end enq" : time, "start in" : time, "end in" : time, "start compute" : time, "end -//! compute" : time, -//! "start out" : time, "in" : time, "compute" : time, "out" : time, "latency" : time, "end to end" : time} +//! value ::= { "start enq : time, "end enq" : time, "start h2d" : time, "end h2d" : time, "start compute" : time, +//! "end compute" : time, "start d2h" : time, "end d2h" : time, "h2d" : time, "compute" : time, +//! "d2h" : time, "latency" : time, "end to end" : time } //! void exportJSONTrace(const std::vector& trace, const std::string& fileName) { @@ -228,19 +282,20 @@ void exportJSONTrace(const std::vector& trace, const std::string const InferenceTime it(traceToTiming(t)); os << sep << "{ "; sep = ", "; - // clang off - os << "\"startEnqMs\" : " << t.inStart << sep << "\"endEnqMs\" : " << t.inEnd << sep - << "\"startInMs\" : " << t.enqStart << sep << "\"endInMs\" : " << t.enqEnd << sep + // clang-format off + os << "\"startEnqMs\" : " << t.enqStart << sep << "\"endEnqMs\" : " << t.enqEnd << sep + << "\"startH2dMs\" : " << t.h2dStart << sep << "\"endH2dMs\" : " << t.h2dEnd << sep << "\"startComputeMs\" : " << t.computeStart << sep << "\"endComputeMs\" : " << t.computeEnd << sep - << "\"startOutMs\" : " << t.outStart << sep << "\"endOutMs\" : " << t.outEnd << sep << "\"inMs\" : " << it.in - << sep << "\"computeMs\" : " << it.compute << sep << "\"outMs\" : " << it.out << sep - << "\"latencyMs\" : " << it.latency() << sep << "\"endToEndMs\" : " << it.e2e << " }" << std::endl; - // clang on + << "\"startD2hMs\" : " << t.d2hStart << sep << "\"endD2hMs\" : " << t.d2hEnd << sep + << "\"h2dMs\" : " << it.h2d << sep << "\"computeMs\" : " << it.compute << sep + << "\"d2hMs\" : " << it.d2h << sep << "\"latencyMs\" : " << it.latency() << sep + << "\"endToEndMs\" : " << it.e2e << " }" << std::endl; + // clang-format on } os << "]" << std::endl; } -void Profiler::reportLayerTime(const char* layerName, float timeMs) +void Profiler::reportLayerTime(const char* layerName, float timeMs) noexcept { if (mIterator == mLayers.end()) { @@ -262,16 +317,19 @@ void Profiler::reportLayerTime(const char* layerName, float timeMs) ++mIterator; } -void Profiler::print(std::ostream& os) const +void Profiler::print(std::ostream& os) const noexcept { const std::string nameHdr("Layer"); const std::string timeHdr(" Time (ms)"); const std::string avgHdr(" Avg. Time (ms)"); - const std::string percentageHdr(" Time \%"); + const std::string percentageHdr(" Time %"); const float totalTimeMs = getTotalTime(); - const auto cmpLayer = [](const LayerProfile& a, const LayerProfile& b) { return a.name.size() < b.name.size(); }; + const auto cmpLayer = [](const LayerProfile& a, const LayerProfile& b) + { + return a.name.size() < b.name.size(); + }; const auto longestName = std::max_element(mLayers.begin(), mLayers.end(), cmpLayer); const auto nameLength = std::max(longestName->name.size() + 1, nameHdr.size()); const auto timeLength = timeHdr.size(); @@ -284,7 +342,7 @@ void Profiler::print(std::ostream& os) const for (const auto& p : mLayers) { - // clang off + // clang-format off os << std::setw(nameLength) << p.name << std::setw(timeLength) << std::fixed << std::setprecision(2) << p.timeMs << std::setw(avgLength) << std::fixed << std::setprecision(4) << p.timeMs / mUpdatesCount << std::setw(percentageLength) << std::fixed << std::setprecision(1) << p.timeMs / totalTimeMs * 100 @@ -294,12 +352,12 @@ void Profiler::print(std::ostream& os) const os << std::setw(nameLength) << "Total" << std::setw(timeLength) << std::fixed << std::setprecision(2) << totalTimeMs << std::setw(avgLength) << std::fixed << std::setprecision(4) << totalTimeMs / mUpdatesCount << std::setw(percentageLength) << std::fixed << std::setprecision(1) << 100.0 << std::endl; - // clang on + // clang-format on } os << std::endl; } -void Profiler::exportJSONProfile(const std::string& fileName) const +void Profiler::exportJSONProfile(const std::string& fileName) const noexcept { std::ofstream os(fileName, std::ofstream::trunc); os << "[" << std::endl << " { \"count\" : " << mUpdatesCount << " }" << std::endl; @@ -308,14 +366,13 @@ void Profiler::exportJSONProfile(const std::string& fileName) const for (const auto& l : mLayers) { - // clang off - os << ", {" - << " \"name\" : \"" << l.name - << "\"" - ", \"timeMs\" : " - << l.timeMs << ", \"averageMs\" : " << l.timeMs / mUpdatesCount - << ", \"percentage\" : " << l.timeMs / totalTimeMs * 100 << " }" << std::endl; - // clang on + // clang-format off + os << ", {" << " \"name\" : \"" << l.name << "\"" + ", \"timeMs\" : " << l.timeMs + << ", \"averageMs\" : " << l.timeMs / mUpdatesCount + << ", \"percentage\" : " << l.timeMs / totalTimeMs * 100 + << " }" << std::endl; + // clang-format on } os << "]" << std::endl; } @@ -332,7 +389,8 @@ void dumpOutputs(const nvinfer1::IExecutionContext& context, const Bindings& bin bindings.dumpOutputs(context, os); } -void exportJSONOutput(const nvinfer1::IExecutionContext& context, const Bindings& bindings, const std::string& fileName) +void exportJSONOutput( + const nvinfer1::IExecutionContext& context, const Bindings& bindings, const std::string& fileName, int32_t batch) { std::ofstream os(fileName, std::ofstream::trunc); std::string sep = " "; @@ -340,16 +398,16 @@ void exportJSONOutput(const nvinfer1::IExecutionContext& context, const Bindings os << "[" << std::endl; for (const auto& binding : output) { - // clang off + // clang-format off os << sep << "{ \"name\" : \"" << binding.first << "\"" << std::endl; sep = ", "; os << " " << sep << "\"dimensions\" : \""; bindings.dumpBindingDimensions(binding.second, context, os); os << "\"" << std::endl; os << " " << sep << "\"values\" : [ "; - bindings.dumpBindingValues(binding.second, os, sep); + bindings.dumpBindingValues(context, binding.second, os, sep, batch); os << " ]" << std::endl << " }" << std::endl; - // clang on + // clang-format on } os << "]" << std::endl; } diff --git a/samples/common/sampleReporting.h b/samples/common/sampleReporting.h index 73621651..d9b624d4 100644 --- a/samples/common/sampleReporting.h +++ b/samples/common/sampleReporting.h @@ -17,6 +17,7 @@ #ifndef TRT_SAMPLE_REPORTING_H #define TRT_SAMPLE_REPORTING_H +#include #include #include "NvInfer.h" @@ -35,9 +36,9 @@ struct InferenceTime { InferenceTime(float q, float i, float c, float o, float e) : enq(q) - , in(i) + , h2d(i) , compute(c) - , out(o) + , d2h(o) , e2e(e) { } @@ -50,15 +51,15 @@ struct InferenceTime ~InferenceTime() = default; float enq{0}; // Enqueue - float in{0}; // Host to Device + float h2d{0}; // Host to Device float compute{0}; // Compute - float out{0}; // Device to Host + float d2h{0}; // Device to Host float e2e{0}; // end to end // ideal latency float latency() const { - return in + compute + out; + return h2d + compute + d2h; } }; @@ -72,12 +73,12 @@ struct InferenceTrace : stream(s) , enqStart(es) , enqEnd(ee) - , inStart(is) - , inEnd(ie) + , h2dStart(is) + , h2dEnd(ie) , computeStart(cs) , computeEnd(ce) - , outStart(os) - , outEnd(oe) + , d2hStart(os) + , d2hEnd(oe) { } @@ -91,17 +92,17 @@ struct InferenceTrace int stream{0}; float enqStart{0}; float enqEnd{0}; - float inStart{0}; - float inEnd{0}; + float h2dStart{0}; + float h2dEnd{0}; float computeStart{0}; float computeEnd{0}; - float outStart{0}; - float outEnd{0}; + float d2hStart{0}; + float d2hEnd{0}; }; inline InferenceTime operator+(const InferenceTime& a, const InferenceTime& b) { - return InferenceTime(a.enq + b.enq, a.in + b.in, a.compute + b.compute, a.out + b.out, a.e2e + b.e2e); + return InferenceTime(a.enq + b.enq, a.h2d + b.h2d, a.compute + b.compute, a.d2h + b.d2h, a.e2e + b.e2e); } inline InferenceTime operator+=(InferenceTime& a, const InferenceTime& b) @@ -109,6 +110,19 @@ inline InferenceTime operator+=(InferenceTime& a, const InferenceTime& b) return a = a + b; } +//! +//! \struct PerformanceResult +//! \brief Performance result of a performance metric +//! +struct PerformanceResult +{ + float min{0}; + float max{0}; + float mean{0}; + float median{0}; + float percentile{0}; +}; + //! //! \brief Print benchmarking time and number of traces collected //! @@ -122,13 +136,25 @@ void printTiming(const std::vector& timings, int runsPerAvg, std: //! //! \brief Print the performance summary of a trace //! -void printEpilog(std::vector timings, float percentile, int queries, std::ostream& os); +void printEpilog(const std::vector& timings, float percentile, int batchSize, std::ostream& osInfo, + std::ostream& osWarning, std::ostream& osVerbose); + +//! +//! \brief Get the result of a specific performance metric from a trace +//! +PerformanceResult getPerformanceResult(const std::vector& timings, + std::function metricGetter, float percentile); + +//! +//! \brief Print the explanations of the performance metrics printed in printEpilog() function. +//! +void printMetricExplanations(std::ostream& os); //! //! \brief Print and summarize a timing trace //! void printPerformanceReport(const std::vector& trace, const ReportingOptions& reporting, float warmupMs, - int queries, std::ostream& os); + int batchSize, std::ostream& osInfo, std::ostream& osWarning, std::ostream& osVerbose); //! //! \brief Export a timing trace to JSON file @@ -149,7 +175,7 @@ void dumpOutputs(const nvinfer1::IExecutionContext& context, const Bindings& bin //! \brief Export output tensors to JSON file //! void exportJSONOutput( - const nvinfer1::IExecutionContext& context, const Bindings& bindings, const std::string& fileName); + const nvinfer1::IExecutionContext& context, const Bindings& bindings, const std::string& fileName, int32_t batch); //! //! \struct LayerProfile @@ -169,17 +195,17 @@ class Profiler : public nvinfer1::IProfiler { public: - void reportLayerTime(const char* layerName, float timeMs) override; + void reportLayerTime(const char* layerName, float timeMs) noexcept override; - void print(std::ostream& os) const; + void print(std::ostream& os) const noexcept; //! //! \brief Export a profile to JSON file //! - void exportJSONProfile(const std::string& fileName) const; + void exportJSONProfile(const std::string& fileName) const noexcept; private: - float getTotalTime() const + float getTotalTime() const noexcept { const auto plusLayerTime = [](float accumulator, const LayerProfile& lp) { return accumulator + lp.timeMs; }; return std::accumulate(mLayers.begin(), mLayers.end(), 0.0, plusLayerTime); diff --git a/samples/common/sampleUtils.h b/samples/common/sampleUtils.h index b3e7f811..8c94b913 100644 --- a/samples/common/sampleUtils.h +++ b/samples/common/sampleUtils.h @@ -66,7 +66,8 @@ inline int volume(const nvinfer1::Dims& d) return std::accumulate(d.d, d.d + d.nbDims, 1, std::multiplies()); } -inline int volume(const nvinfer1::Dims& dims, const nvinfer1::Dims& strides, int vecDim, int comps, int batch) +//! comps is the number of components in a vector. Ignored if vecDim < 0. +inline int64_t volume(const nvinfer1::Dims& dims, const nvinfer1::Dims& strides, int vecDim, int comps, int batch) { int maxNbElems = 1; for (int i = 0; i < dims.nbDims; ++i) @@ -84,10 +85,10 @@ inline int volume(const nvinfer1::Dims& dims, const nvinfer1::Dims& strides, int } maxNbElems = std::max(maxNbElems, d * strides.d[i]); } - return maxNbElems * batch * (vecDim < 0 ? 1 : comps); + return static_cast(maxNbElems) * batch * (vecDim < 0 ? 1 : comps); } -inline int volume(nvinfer1::Dims dims, int vecDim, int comps, int batch) +inline int64_t volume(nvinfer1::Dims dims, int vecDim, int comps, int batch) { if (vecDim != -1) { @@ -104,16 +105,6 @@ inline std::ostream& operator<<(std::ostream& os, const nvinfer1::Dims& dims) } return os; } - -inline std::ostream& operator<<(std::ostream& os, const std::vector& vec) -{ - for (int i = 0, e = static_cast(vec.size()); i < e; ++i) - { - os << (i ? "x" : "") << vec[i]; - } - return os; -} - inline std::ostream& operator<<(std::ostream& os, const nvinfer1::WeightsRole role) { switch (role) @@ -143,11 +134,25 @@ inline std::ostream& operator<<(std::ostream& os, const nvinfer1::WeightsRole ro os << "Constant"; break; } + case nvinfer1::WeightsRole::kANY: + { + os << "Any"; + break; + } } return os; } +inline std::ostream& operator<<(std::ostream& os, const std::vector& vec) +{ + for (int i = 0, e = static_cast(vec.size()); i < e; ++i) + { + os << (i ? "x" : "") << vec[i]; + } + return os; +} + inline nvinfer1::Dims toDims(const std::vector& vec) { int limit = static_cast(nvinfer1::Dims::MAX_DIMS); @@ -156,13 +161,13 @@ inline nvinfer1::Dims toDims(const std::vector& vec) sample::gLogWarning << "Vector too long, only first 8 elements are used in dimension." << std::endl; } // Pick first nvinfer1::Dims::MAX_DIMS elements - nvinfer1::Dims dims{std::min(static_cast(vec.size()), limit), {}, {}}; + nvinfer1::Dims dims{std::min(static_cast(vec.size()), limit), {}}; std::copy_n(vec.begin(), dims.nbDims, std::begin(dims.d)); return dims; } template -inline void fillBuffer(void* buffer, int volume, T min, T max) +inline void fillBuffer(void* buffer, int64_t volume, T min, T max) { T* typedBuffer = static_cast(buffer); std::default_random_engine engine; @@ -182,7 +187,7 @@ inline void fillBuffer(void* buffer, int volume, T min, T max) // Specialization needed for custom type __half template -inline void fillBufferHalf(void* buffer, int volume, H min, H max) +inline void fillBufferHalf(void* buffer, int64_t volume, H min, H max) { H* typedBuffer = static_cast(buffer); std::default_random_engine engine; @@ -192,22 +197,41 @@ inline void fillBufferHalf(void* buffer, int volume, H min, H max) } template <> #if CUDA_VERSION < 10000 -inline void fillBuffer(void* buffer, int volume, half_float::half min, half_float::half max) +inline void fillBuffer(void* buffer, int64_t volume, half_float::half min, half_float::half max) #else -inline void fillBuffer<__half>(void* buffer, int volume, __half min, __half max) +inline void fillBuffer<__half>(void* buffer, int64_t volume, __half min, __half max) #endif { fillBufferHalf(buffer, volume, min, max); } template -inline void dumpBuffer(const void* buffer, int volume, const std::string& separator, std::ostream& os) +inline void dumpBuffer(const void* buffer, const std::string& separator, std::ostream& os, const Dims& dims, + const Dims& strides, int32_t vectorDim, int32_t spv) { + const int64_t volume = std::accumulate(dims.d, dims.d + dims.nbDims, 1, std::multiplies()); const T* typedBuffer = static_cast(buffer); std::string sep; - for (int v = 0; v < volume; ++v) + for (int64_t v = 0; v < volume; ++v) { - os << sep << typedBuffer[v]; + int64_t curV = v; + int32_t dataOffset = 0; + for (int32_t dimIndex = dims.nbDims - 1; dimIndex >= 0; --dimIndex) + { + int32_t dimVal = curV % dims.d[dimIndex]; + if (dimIndex == vectorDim) + { + dataOffset += (dimVal / spv) * strides.d[dimIndex] * spv + dimVal % spv; + } + else + { + dataOffset += dimVal * strides.d[dimIndex] * (vectorDim == -1 ? 1 : spv); + } + curV /= dims.d[dimIndex]; + ASSERT(curV >= 0); + } + + os << sep << typedBuffer[dataOffset]; sep = separator; } } @@ -216,7 +240,7 @@ struct Binding { bool isInput{false}; MirroredBuffer buffer; - int volume{0}; + int64_t volume{0}; nvinfer1::DataType dataType{nvinfer1::DataType::kFLOAT}; void fill(const std::string& fileName) @@ -253,7 +277,8 @@ struct Binding fillBuffer(buffer.getHostBuffer(), volume, -1.0, 1.0); break; } - case nvinfer1::DataType::kHALF: { + case nvinfer1::DataType::kHALF: + { #if CUDA_VERSION < 10000 fillBuffer(buffer.getHostBuffer(), volume, static_cast(-1.0), static_cast(-1.0)); @@ -265,35 +290,37 @@ struct Binding } } - void dump(std::ostream& os, const std::string separator = " ") const + void dump(std::ostream& os, Dims dims, Dims strides, int32_t vectorDim, int32_t spv, + const std::string separator = " ") const { switch (dataType) { case nvinfer1::DataType::kBOOL: { - dumpBuffer(buffer.getHostBuffer(), volume, separator, os); + dumpBuffer(buffer.getHostBuffer(), separator, os, dims, strides, vectorDim, spv); break; } case nvinfer1::DataType::kINT32: { - dumpBuffer(buffer.getHostBuffer(), volume, separator, os); + dumpBuffer(buffer.getHostBuffer(), separator, os, dims, strides, vectorDim, spv); break; } case nvinfer1::DataType::kINT8: { - dumpBuffer(buffer.getHostBuffer(), volume, separator, os); + dumpBuffer(buffer.getHostBuffer(), separator, os, dims, strides, vectorDim, spv); break; } case nvinfer1::DataType::kFLOAT: { - dumpBuffer(buffer.getHostBuffer(), volume, separator, os); + dumpBuffer(buffer.getHostBuffer(), separator, os, dims, strides, vectorDim, spv); break; } - case nvinfer1::DataType::kHALF: { + case nvinfer1::DataType::kHALF: + { #if CUDA_VERSION < 10000 - dumpBuffer(buffer.getHostBuffer(), volume, separator, os); + dumpBuffer(buffer.getHostBuffer(), separator, os, dims, strides, vectorDim, spv); #else - dumpBuffer<__half>(buffer.getHostBuffer(), volume, separator, os); + dumpBuffer<__half>(buffer.getHostBuffer(), separator, os, dims, strides, vectorDim, spv); #endif break; } @@ -304,7 +331,7 @@ struct Binding class Bindings { public: - void addBinding(int b, const std::string& name, bool isInput, int volume, nvinfer1::DataType dataType, + void addBinding(int b, const std::string& name, bool isInput, int64_t volume, nvinfer1::DataType dataType, const std::string& fileName = "") { while (mBindings.size() <= static_cast(b)) @@ -314,7 +341,16 @@ public: } mNames[name] = b; mBindings[b].isInput = isInput; - mBindings[b].buffer.allocate(static_cast(volume) * static_cast(dataTypeSize(dataType))); + // Some memory allocators return nullptr when allocating zero bytes, but TensorRT requires a non-null ptr + // even for empty tensors, so allocate a dummy byte. + if (volume == 0) + { + mBindings[b].buffer.allocate(1); + } + else + { + mBindings[b].buffer.allocate(static_cast(volume) * static_cast(dataTypeSize(dataType))); + } mBindings[b].volume = volume; mBindings[b].dataType = dataType; mDevicePointers[b] = mBindings[b].buffer.getDeviceBuffer(); @@ -375,9 +411,37 @@ public: os << dims; } - void dumpBindingValues(int binding, std::ostream& os, const std::string& separator = " ") const + void dumpBindingValues(const nvinfer1::IExecutionContext& context, int binding, std::ostream& os, + const std::string& separator = " ", int32_t batch = 1) const { - mBindings[binding].dump(os, separator); + Dims dims = context.getBindingDimensions(binding); + Dims strides = context.getStrides(binding); + int32_t vectorDim = context.getEngine().getBindingVectorizedDim(binding); + const int32_t spv = context.getEngine().getBindingComponentsPerElement(binding); + + if (context.getEngine().hasImplicitBatchDimension()) + { + auto insertN = [](Dims& d, int32_t bs) { + const int32_t nbDims = d.nbDims; + ASSERT(nbDims < Dims::MAX_DIMS); + std::copy_backward(&d.d[0], &d.d[nbDims], &d.d[nbDims + 1]); + d.d[0] = bs; + d.nbDims = nbDims + 1; + }; + int32_t batchStride = 0; + for (int32_t i = 0; i < strides.nbDims; ++i) + { + if (strides.d[i] * dims.d[i] > batchStride) + { + batchStride = strides.d[i] * dims.d[i]; + } + } + insertN(dims, batch); + insertN(strides, batchStride); + vectorDim = (vectorDim == -1) ? -1 : vectorDim + 1; + } + + mBindings[binding].dump(os, dims, strides, vectorDim, spv, separator); } void dumpInputs(const nvinfer1::IExecutionContext& context, std::ostream& os) const @@ -409,7 +473,8 @@ public: os << n.first << ": ("; dumpBindingDimensions(binding, context, os); os << ")" << std::endl; - dumpBindingValues(binding, os); + + dumpBindingValues(context, binding, os); os << std::endl; } } @@ -485,6 +550,38 @@ inline bool broadcastIOFormats(const std::vector& formats, size_t nbBi return broadcast; } +inline std::vector loadTimingCacheFile(const std::string inFileName) +{ + std::ifstream iFile(inFileName, std::ios::in | std::ios::binary); + if (!iFile) + { + sample::gLogWarning << "Could not read timing cache from: " << inFileName + << ". A new timing cache will be generated and written." << std::endl; + return std::vector(); + } + iFile.seekg(0, std::ifstream::end); + size_t fsize = iFile.tellg(); + iFile.seekg(0, std::ifstream::beg); + std::vector content(fsize); + iFile.read(content.data(), fsize); + iFile.close(); + sample::gLogInfo << "Loaded " << fsize << " bytes of timing cache from " << inFileName << std::endl; + return content; +} + +inline void saveTimingCacheFile(const std::string outFileName, const IHostMemory* blob) +{ + std::ofstream oFile(outFileName, std::ios::out | std::ios::binary); + if (!oFile) + { + sample::gLogWarning << "Could not write timing cache to: " << outFileName << std::endl; + return; + } + oFile.write((char*) blob->data(), blob->size()); + oFile.close(); + sample::gLogInfo << "Saved " << blob->size() << " bytes of timing cache to " << outFileName << std::endl; +} + } // namespace sample #endif // TRT_SAMPLE_UTILS_H diff --git a/samples/opensource/CMakeLists.txt b/samples/opensource/CMakeLists.txt index b30b6496..c23d54a6 100644 --- a/samples/opensource/CMakeLists.txt +++ b/samples/opensource/CMakeLists.txt @@ -24,11 +24,8 @@ set(OPENSOURCE_SAMPLES_LIST sampleMLP sampleMNIST sampleMNISTAPI - sampleMovieLens - sampleMovieLensMPS sampleNMT sampleOnnxMNIST - samplePlugin sampleReformatFreeIO sampleSSD sampleUffFasterRCNN diff --git a/samples/opensource/sampleAlgorithmSelector/sampleAlgorithmSelector.cpp b/samples/opensource/sampleAlgorithmSelector/sampleAlgorithmSelector.cpp index 4c3a1b49..85d95da4 100644 --- a/samples/opensource/sampleAlgorithmSelector/sampleAlgorithmSelector.cpp +++ b/samples/opensource/sampleAlgorithmSelector/sampleAlgorithmSelector.cpp @@ -33,7 +33,6 @@ #include "NvInfer.h" #include -#include #include #include #include @@ -43,6 +42,8 @@ #include #include +using samplesCommon::SampleUniquePtr; + const std::string gSampleName = "TensorRT.sample_algorithm_selector"; const std::string gCacheFileName = "AlgorithmCache.txt"; //! @@ -59,10 +60,10 @@ public: //! If BuilderFlag::kSTRICT_TYPES is not set, just returning 0 forces default tactic selection. //! int32_t selectAlgorithms(const nvinfer1::IAlgorithmContext& context, const nvinfer1::IAlgorithm* const* choices, - int32_t nbChoices, int32_t* selection) override + int32_t nbChoices, int32_t* selection) noexcept override { // TensorRT always provides more than zero number of algorithms in selectAlgorithms. - assert(nbChoices > 0); + ASSERT(nbChoices > 0); std::iota(selection, selection + nbChoices, 0); return nbChoices; @@ -98,8 +99,10 @@ public: // Write input and output formats. for (int32_t j = 0; j < nbInputs + nbOutputs; j++) { - algorithmFile << static_cast(algoChoices[i]->getAlgorithmIOInfo(j).getTensorFormat()) << "\n"; - algorithmFile << static_cast(algoChoices[i]->getAlgorithmIOInfo(j).getDataType()) << "\n"; + algorithmFile << static_cast(algoChoices[i]->getAlgorithmIOInfoByIndex(j)->getTensorFormat()) + << "\n"; + algorithmFile << static_cast(algoChoices[i]->getAlgorithmIOInfoByIndex(j)->getDataType()) + << "\n"; } } algorithmFile.close(); @@ -126,10 +129,10 @@ public: //! \details Use the map created from cache to select algorithms. //! int32_t selectAlgorithms(const nvinfer1::IAlgorithmContext& algoContext, - const nvinfer1::IAlgorithm* const* algoChoices, int32_t nbChoices, int32_t* selection) override + const nvinfer1::IAlgorithm* const* algoChoices, int32_t nbChoices, int32_t* selection) noexcept override { // TensorRT always provides more than zero number of algorithms in selectAlgorithms. - assert(nbChoices > 0); + ASSERT(nbChoices > 0); const std::string layerName(algoContext.getName()); auto it = choiceMap.find(layerName); @@ -137,11 +140,11 @@ public: // The layerName can be used as a unique identifier for a layer. // Since the network and config has not been changed (between the cache and cache read), // This map must contain layerName. - assert(it != choiceMap.end()); + ASSERT(it != choiceMap.end()); auto& algoItem = it->second; - assert(algoItem.nbInputs == algoContext.getNbInputs()); - assert(algoItem.nbOutputs == algoContext.getNbOutputs()); + ASSERT(algoItem.nbInputs == algoContext.getNbInputs()); + ASSERT(algoItem.nbOutputs == algoContext.getNbOutputs()); int32_t nbSelections = 0; for (auto i = 0; i < nbChoices; i++) @@ -156,7 +159,7 @@ public: } //! There must be only one algorithm selected. - assert(nbSelections == 1); + ASSERT(nbSelections == 1); return nbSelections; } @@ -166,24 +169,24 @@ public: //! \details Verifies that the algorithm used by TensorRT conform to the cache. //! void reportAlgorithms(const nvinfer1::IAlgorithmContext* const* algoContexts, - const nvinfer1::IAlgorithm* const* algoChoices, int32_t nbAlgorithms) override + const nvinfer1::IAlgorithm* const* algoChoices, int32_t nbAlgorithms) noexcept override { for (auto i = 0; i < nbAlgorithms; i++) { const std::string layerName(algoContexts[i]->getName()); - assert(choiceMap.find(layerName) != choiceMap.end()); + ASSERT(choiceMap.find(layerName) != choiceMap.end()); const auto& algoItem = choiceMap[layerName]; - assert(algoItem.nbInputs == algoContexts[i]->getNbInputs()); - assert(algoItem.nbOutputs == algoContexts[i]->getNbOutputs()); - assert(algoChoices[i]->getAlgorithmVariant().getImplementation() == algoItem.implementation); - assert(algoChoices[i]->getAlgorithmVariant().getTactic() == algoItem.tactic); + ASSERT(algoItem.nbInputs == algoContexts[i]->getNbInputs()); + ASSERT(algoItem.nbOutputs == algoContexts[i]->getNbOutputs()); + ASSERT(algoChoices[i]->getAlgorithmVariant().getImplementation() == algoItem.implementation); + ASSERT(algoChoices[i]->getAlgorithmVariant().getTactic() == algoItem.tactic); auto nbFormats = algoItem.nbInputs + algoItem.nbOutputs; for (auto j = 0; j < nbFormats; j++) { - assert(algoItem.formats[j].first - == static_cast(algoChoices[i]->getAlgorithmIOInfo(j).getTensorFormat())); - assert(algoItem.formats[j].second - == static_cast(algoChoices[i]->getAlgorithmIOInfo(j).getDataType())); + ASSERT(algoItem.formats[j].first + == static_cast(algoChoices[i]->getAlgorithmIOInfoByIndex(j)->getTensorFormat())); + ASSERT(algoItem.formats[j].second + == static_cast(algoChoices[i]->getAlgorithmIOInfoByIndex(j)->getDataType())); } } } @@ -244,7 +247,7 @@ private: //! The combination of implementation, tactic and input/output formats is unique to an algorithm, //! and can be used to check if two algorithms are same. - static bool areSame(const AlgorithmCacheItem& algoCacheItem, const IAlgorithm& algoChoice) + static bool areSame(const AlgorithmCacheItem& algoCacheItem, const IAlgorithm& algoChoice) noexcept { if (algoChoice.getAlgorithmVariant().getImplementation() != algoCacheItem.implementation || algoChoice.getAlgorithmVariant().getTactic() != algoCacheItem.tactic) @@ -257,9 +260,9 @@ private: for (auto j = 0; j < nbFormats; j++) { if (algoCacheItem.formats[j].first - != static_cast(algoChoice.getAlgorithmIOInfo(j).getTensorFormat()) + != static_cast(algoChoice.getAlgorithmIOInfoByIndex(j)->getTensorFormat()) || algoCacheItem.formats[j].second - != static_cast(algoChoice.getAlgorithmIOInfo(j).getDataType())) + != static_cast(algoChoice.getAlgorithmIOInfoByIndex(j)->getDataType())) { return false; } @@ -281,12 +284,12 @@ public: //! \details Use the map created from cache to select algorithms. //! int32_t selectAlgorithms(const nvinfer1::IAlgorithmContext& algoContext, - const nvinfer1::IAlgorithm* const* algoChoices, int32_t nbChoices, int32_t* selection) override + const nvinfer1::IAlgorithm* const* algoChoices, int32_t nbChoices, int32_t* selection) noexcept override { // TensorRT always provides more than zero number of algorithms in selectAlgorithms. - assert(nbChoices > 0); + ASSERT(nbChoices > 0); - auto it = std::min_element( + const auto* it = std::min_element( algoChoices, algoChoices + nbChoices, [](const nvinfer1::IAlgorithm* x, const nvinfer1::IAlgorithm* y) { return x->getWorkspaceSize() < y->getWorkspaceSize(); }); @@ -298,7 +301,7 @@ public: //! \brief Called by TensorRT to report choices it made. //! void reportAlgorithms(const nvinfer1::IAlgorithmContext* const* algoContexts, - const nvinfer1::IAlgorithm* const* algoChoices, int32_t nbAlgorithms) override + const nvinfer1::IAlgorithm* const* algoChoices, int32_t nbAlgorithms) noexcept override { // do nothing } @@ -311,9 +314,6 @@ public: //! class SampleAlgorithmSelector { - template - using SampleUniquePtr = std::unique_ptr; - public: SampleAlgorithmSelector(const samplesCommon::CaffeSampleParams& params) : mParams(params) @@ -380,7 +380,7 @@ bool SampleAlgorithmSelector::build(IAlgorithmSelector* selector) return false; } - auto network = SampleUniquePtr(builder->createNetwork()); + auto network = SampleUniquePtr(builder->createNetworkV2(0)); if (!network) { return false; @@ -406,13 +406,7 @@ bool SampleAlgorithmSelector::build(IAlgorithmSelector* selector) builder->setMaxBatchSize(mParams.batchSize); config->setMaxWorkspaceSize(16_MiB); config->setAlgorithmSelector(selector); - config->setFlag(BuilderFlag::kGPU_FALLBACK); - if (!mParams.int8) - { - // The sample fails for Int8 with kSTRICT_TYPES flag set. - config->setFlag(BuilderFlag::kSTRICT_TYPES); - } if (mParams.fp16) { config->setFlag(BuilderFlag::kFP16); @@ -422,18 +416,44 @@ bool SampleAlgorithmSelector::build(IAlgorithmSelector* selector) config->setFlag(BuilderFlag::kINT8); } - samplesCommon::enableDLA(builder.get(), config.get(), mParams.dlaCore); - mEngine = std::shared_ptr( - builder->buildEngineWithConfig(*network, *config), samplesCommon::InferDeleter()); + samplesCommon::enableDLA(builder.get(), config.get(), mParams.dlaCore, true /*GPUFallback*/); + if (mParams.int8) + { + // The sample fails for Int8 with kSTRICT_TYPES flag set. + config->clearFlag(BuilderFlag::kSTRICT_TYPES); + } + + SampleUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + if (!runtime) + { + return false; + } + + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { + return false; + } + config->setProfileStream(*profileStream); + + SampleUniquePtr plan{builder->buildSerializedNetwork(*network, *config)}; + if (!plan) + { + return false; + } + + mEngine = std::shared_ptr( + runtime->deserializeCudaEngine(plan->data(), plan->size()), samplesCommon::InferDeleter()); if (!mEngine) { return false; } - assert(network->getNbInputs() == 1); + ASSERT(network->getNbInputs() == 1); mInputDims = network->getInput(0)->getDimensions(); - assert(mInputDims.nbDims == 3); + ASSERT(mInputDims.nbDims == 3); return true; } @@ -480,7 +500,7 @@ bool SampleAlgorithmSelector::verifyOutput( // Print histogram of the output distribution. sample::gLogInfo << "Output:\n"; - float val{0.0f}; + float val{0.0F}; int idx{0}; const int kDIGITS = 10; @@ -492,11 +512,11 @@ bool SampleAlgorithmSelector::verifyOutput( idx = i; } - sample::gLogInfo << i << ": " << std::string(int(std::floor(prob[i] * 10 + 0.5f)), '*') << "\n"; + sample::gLogInfo << i << ": " << std::string(int(std::floor(prob[i] * 10 + 0.5F)), '*') << "\n"; } sample::gLogInfo << std::endl; - return (idx == groundTruthDigit && val > 0.9f); + return (idx == groundTruthDigit && val > 0.9F); } //! @@ -532,7 +552,7 @@ bool SampleAlgorithmSelector::constructNetwork( float maxMean = samplesCommon::getMaxValue(static_cast(meanWeights.values), samplesCommon::volume(inputDims)); - auto mean = network->addConstant(nvinfer1::Dims3(1, inputDims.d[1], inputDims.d[2]), meanWeights); + auto* mean = network->addConstant(nvinfer1::Dims3(1, inputDims.d[1], inputDims.d[2]), meanWeights); if (!mean->getOutput(0)->setDynamicRange(-maxMean, maxMean)) { return false; @@ -541,13 +561,13 @@ bool SampleAlgorithmSelector::constructNetwork( { return false; } - auto meanSub = network->addElementWise(*network->getInput(0), *mean->getOutput(0), ElementWiseOperation::kSUB); + auto* meanSub = network->addElementWise(*network->getInput(0), *mean->getOutput(0), ElementWiseOperation::kSUB); if (!meanSub->getOutput(0)->setDynamicRange(-maxMean, maxMean)) { return false; } network->getLayer(0)->setInput(0, *meanSub->getOutput(0)); - samplesCommon::setAllTensorScales(network.get(), 127.0f, 127.0f); + samplesCommon::setAllDynamicRanges(network.get(), 127.0F, 127.0F); return true; } @@ -575,7 +595,7 @@ bool SampleAlgorithmSelector::infer() // Read the input data into the managed buffers. // There should be just 1 input tensor. - assert(mParams.inputTensorNames.size() == 1); + ASSERT(mParams.inputTensorNames.size() == 1); if (!processInput(buffers, mParams.inputTensorNames[0], digit)) { return false; @@ -603,7 +623,7 @@ bool SampleAlgorithmSelector::infer() // Check and print the output of the inference. // There should be just one output tensor. - assert(mParams.outputTensorNames.size() == 1); + ASSERT(mParams.outputTensorNames.size() == 1); bool outputCorrect = verifyOutput(buffers, mParams.outputTensorNames[0], digit); return outputCorrect; @@ -685,9 +705,9 @@ int main(int argc, char** argv) return EXIT_SUCCESS; } - auto sampleTest = sample::gLogger.defineTest(gSampleName, argc, argv); + auto sampleTest = sample::Logger::defineTest(gSampleName, argc, argv); - sample::gLogger.reportTestStart(sampleTest); + sample::Logger::reportTestStart(sampleTest); samplesCommon::CaffeSampleParams params = initializeSampleParams(args); @@ -701,12 +721,12 @@ int main(int argc, char** argv) if (!sampleAlgorithmSelector.build(&algorithmCacheWriter)) { - return sample::gLogger.reportFail(sampleTest); + return sample::Logger::reportFail(sampleTest); } if (!sampleAlgorithmSelector.infer()) { - return sample::gLogger.reportFail(sampleTest); + return sample::Logger::reportFail(sampleTest); } } @@ -717,12 +737,12 @@ int main(int argc, char** argv) if (!sampleAlgorithmSelector.build(&algorithmCacheReader)) { - return sample::gLogger.reportFail(sampleTest); + return sample::Logger::reportFail(sampleTest); } if (!sampleAlgorithmSelector.infer()) { - return sample::gLogger.reportFail(sampleTest); + return sample::Logger::reportFail(sampleTest); } } @@ -734,19 +754,19 @@ int main(int argc, char** argv) MinimumWorkspaceAlgorithmSelector minimumWorkspaceAlgorithmSelector; if (!sampleAlgorithmSelector.build(&minimumWorkspaceAlgorithmSelector)) { - return sample::gLogger.reportFail(sampleTest); + return sample::Logger::reportFail(sampleTest); } if (!sampleAlgorithmSelector.infer()) { - return sample::gLogger.reportFail(sampleTest); + return sample::Logger::reportFail(sampleTest); } } if (!sampleAlgorithmSelector.teardown()) { - return sample::gLogger.reportFail(sampleTest); + return sample::Logger::reportFail(sampleTest); } - return sample::gLogger.reportPass(sampleTest); + return sample::Logger::reportPass(sampleTest); } diff --git a/samples/opensource/sampleCharRNN/sampleCharRNN.cpp b/samples/opensource/sampleCharRNN/sampleCharRNN.cpp index e6314c96..c31952e7 100644 --- a/samples/opensource/sampleCharRNN/sampleCharRNN.cpp +++ b/samples/opensource/sampleCharRNN/sampleCharRNN.cpp @@ -25,7 +25,6 @@ #include #include -#include #include #include #include @@ -47,6 +46,8 @@ #include "logger.h" #include "sampleEngines.h" +using samplesCommon::SampleUniquePtr; + const std::string gSampleName = "TensorRT.sample_char_rnn"; static const std::array INDICES{0, 1, 2, 3}; @@ -142,13 +143,11 @@ struct SampleCharRNNParams : samplesCommon::SampleParams class SampleCharRNNBase { public: - template - using SampleUniquePtr = std::unique_ptr; - SampleCharRNNBase(const SampleCharRNNParams& params) : mParams(params) { } + virtual ~SampleCharRNNBase() = default; //! @@ -183,7 +182,7 @@ protected: nvinfer1::Weights convertRNNBias(nvinfer1::Weights input); std::map mWeightMap; - std::vector> weightsMemory; + std::vector> weightsMemory; SampleCharRNNParams mParams; nvinfer1::ITensor* addReshape( @@ -232,7 +231,7 @@ protected: //! //! \brief Add inputs to the TensorRT network and configure LSTM layers using network definition API. //! - nvinfer1::ILayer* addLSTMLayers(SampleCharRNNBase::SampleUniquePtr& network) final; + nvinfer1::ILayer* addLSTMLayers(SampleUniquePtr& network) final; }; class SampleCharRNNLoop : public SampleCharRNNBase @@ -263,7 +262,7 @@ protected: //! //! \brief Add inputs to the TensorRT network and configure LSTM layers using network definition API. //! - nvinfer1::ILayer* addLSTMLayers(SampleCharRNNBase::SampleUniquePtr& network) final; + nvinfer1::ILayer* addLSTMLayers(SampleUniquePtr& network) final; private: nvinfer1::ILayer* addLSTMCell(SampleUniquePtr& network, const LstmIO& inputTensors, @@ -311,9 +310,17 @@ bool SampleCharRNNBase::build() mWeightMap = SampleCharRNNBase::loadWeights(mParams.weightFileName); - config->setMaxWorkspaceSize(32_MiB); + config->setMaxWorkspaceSize(40_MiB); config->setFlag(BuilderFlag::kGPU_FALLBACK); + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { + return false; + } + config->setProfileStream(*profileStream); + constructNetwork(builder, network, config); } else @@ -713,7 +720,9 @@ nvinfer1::ILayer* SampleCharRNNv2::addLSTMLayers(SampleUniquePtr(rnnwL1.values); const float* biasesL1 = static_cast(rnnbL1.values); size_t kernelOffsetL0 = 0, kernelOffsetL1 = 0, biasOffset = 0; - for (int gateIndex = 0, numGates = gateOrder.size(); gateIndex < 2 * numGates; gateIndex++) + const int numGates = gateOrder.size(); + ASSERT(numGates > 0); + for (int gateIndex = 0; gateIndex < 2 * numGates; gateIndex++) { bool isW = (gateIndex < numGates); int64_t weightCountL0 = (isW ? mParams.dataSize : mParams.hiddenSize) * mParams.hiddenSize; @@ -773,7 +782,8 @@ void SampleCharRNNBase::constructNetwork(SampleUniquePtr& bu nvinfer1::Dims2(mParams.vocabSize, mParams.hiddenSize), mWeightMap[mParams.weightNames.FCW_NAME]); // Add matrix multiplication layer for multiplying rnn output with FC weights - auto matrixMultLayer = network->addMatrixMultiply(*fcwts->getOutput(0), false, *rnn->getOutput(0), true); + auto matrixMultLayer = network->addMatrixMultiply( + *fcwts->getOutput(0), MatrixOperation::kNONE, *rnn->getOutput(0), MatrixOperation::kTRANSPOSE); ASSERT(matrixMultLayer != nullptr); matrixMultLayer->getOutput(0)->setName("Matrix Multiplicaton output"); @@ -796,8 +806,20 @@ void SampleCharRNNBase::constructNetwork(SampleUniquePtr& bu sample::gLogInfo << "Done constructing network..." << std::endl; + SampleUniquePtr plan{builder->buildSerializedNetwork(*network, *config)}; + if (!plan) + { + return; + } + + SampleUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + if (!runtime) + { + return; + } + mEngine = std::shared_ptr( - builder->buildEngineWithConfig(*network, *config), samplesCommon::InferDeleter()); + runtime->deserializeCudaEngine(plan->data(), plan->size()), samplesCommon::InferDeleter()); } //! diff --git a/samples/opensource/sampleDynamicReshape/sampleDynamicReshape.cpp b/samples/opensource/sampleDynamicReshape/sampleDynamicReshape.cpp index c7e813d6..a0a44101 100644 --- a/samples/opensource/sampleDynamicReshape/sampleDynamicReshape.cpp +++ b/samples/opensource/sampleDynamicReshape/sampleDynamicReshape.cpp @@ -35,6 +35,8 @@ #include #include +using samplesCommon::SampleUniquePtr; + const std::string gSampleName = "TensorRT.sample_dynamic_reshape"; //! \brief The SampleDynamicReshape class implementes the dynamic reshape sample. @@ -44,9 +46,6 @@ const std::string gSampleName = "TensorRT.sample_dynamic_reshape"; //! class SampleDynamicReshape { - template - using SampleUniquePtr = std::unique_ptr; - public: SampleDynamicReshape(const samplesCommon::OnnxSampleParams& params) : mParams(params) @@ -69,8 +68,10 @@ public: bool infer(); private: - bool buildPreprocessorEngine(const SampleUniquePtr& builder); - bool buildPredictionEngine(const SampleUniquePtr& builder); + bool buildPreprocessorEngine(const SampleUniquePtr& builder, + const SampleUniquePtr& runtime, cudaStream_t profileStream); + bool buildPredictionEngine(const SampleUniquePtr& builder, + const SampleUniquePtr& runtime, cudaStream_t profileStream); Dims loadPGMFile(const std::string& fileName); bool validateOutput(int digit); @@ -80,7 +81,7 @@ private: nvinfer1::Dims mPredictionInputDims; //!< The dimensions of the input of the MNIST model. nvinfer1::Dims mPredictionOutputDims; //!< The dimensions of the output of the MNIST model. - // Engines used for inference. The first is used for resizing inputs, the second for prediction. + // Engine plan files used for inference. One for resizing inputs, another for prediction. SampleUniquePtr mPreprocessorEngine{nullptr}, mPredictionEngine{nullptr}; SampleUniquePtr mPreprocessorContext{nullptr}, mPredictionContext{nullptr}; @@ -114,9 +115,34 @@ bool SampleDynamicReshape::build() sample::gLogError << "Create inference builder failed." << std::endl; return false; } + + auto runtime = makeUnique(nvinfer1::createInferRuntime(sample::gLogger.getTRTLogger())); + if (!runtime) + { + sample::gLogError << "Runtime object creation failed." << std::endl; + return false; + } + // This function will also set mPredictionInputDims and mPredictionOutputDims, // so it needs to be called before building the preprocessor. - return buildPredictionEngine(builder) && buildPreprocessorEngine(builder); + try + { + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { + return false; + } + + bool result = buildPredictionEngine(builder, runtime, *profileStream) + && buildPreprocessorEngine(builder, runtime, *profileStream); + return result; + } + catch (std::runtime_error& e) + { + sample::gLogError << e.what() << std::endl; + return false; + } } //! @@ -124,7 +150,8 @@ bool SampleDynamicReshape::build() //! //! \return Ruturns false if error in build preprocessor engine. //! -bool SampleDynamicReshape::buildPreprocessorEngine(const SampleUniquePtr& builder) +bool SampleDynamicReshape::buildPreprocessorEngine(const SampleUniquePtr& builder, + const SampleUniquePtr& runtime, cudaStream_t profileStream) { // Create the preprocessor engine using a network that supports full dimensions (createNetworkV2). auto preprocessorNetwork = makeUnique( @@ -167,6 +194,7 @@ bool SampleDynamicReshape::buildPreprocessorEngine(const SampleUniquePtrsetDimensions(input->getName(), OptProfileSelector::kOPT, Dims4{calibBatchSize, 1, 28, 28}); profileCalib->setDimensions(input->getName(), OptProfileSelector::kMAX, Dims4{calibBatchSize, 1, 28, 28}); preprocessorConfig->setCalibrationProfile(profileCalib); + preprocessorConfig->setProfileStream(profileStream); std::unique_ptr calibrator; if (mParams.int8) @@ -180,12 +208,22 @@ bool SampleDynamicReshape::buildPreprocessorEngine(const SampleUniquePtrsetInt8Calibrator(calibrator.get()); } - mPreprocessorEngine = makeUnique(builder->buildEngineWithConfig(*preprocessorNetwork, *preprocessorConfig)); - if (!mPreprocessorEngine) + SampleUniquePtr preprocessorPlan = makeUnique( + builder->buildSerializedNetwork(*preprocessorNetwork, *preprocessorConfig)); + if (!preprocessorPlan) { - sample::gLogError << "Preprocessor engine build failed." << std::endl; + sample::gLogError << "Preprocessor serialized engine build failed." << std::endl; return false; } + + mPreprocessorEngine = makeUnique( + runtime->deserializeCudaEngine(preprocessorPlan->data(), preprocessorPlan->size())); + if (!mPreprocessorEngine) + { + sample::gLogError << "Preprocessor engine deserialization failed." << std::endl; + return false; + } + sample::gLogInfo << "Profile dimensions in preprocessor engine:" << std::endl; sample::gLogInfo << " Minimum = " << mPreprocessorEngine->getProfileDimensions(0, 0, OptProfileSelector::kMIN) << std::endl; @@ -193,6 +231,8 @@ bool SampleDynamicReshape::buildPreprocessorEngine(const SampleUniquePtrdeserializeCudaEngine(predictionPlan->data(), predictionPlan->size())); + if (!mPredictionEngine) + { + sample::gLogError << "Prediction engine deserialization failed." << std::endl; + return false; + } + return true; } @@ -297,6 +348,7 @@ bool SampleDynamicReshape::prepare() return false; } + mPredictionContext = makeUnique(mPredictionEngine->createExecutionContext()); if (!mPredictionContext) { @@ -371,7 +423,7 @@ bool SampleDynamicReshape::infer() Dims SampleDynamicReshape::loadPGMFile(const std::string& fileName) { std::ifstream infile(fileName, std::ifstream::binary); - assert(infile.is_open() && "Attempting to read from a file that is not open."); + ASSERT(infile.is_open() && "Attempting to read from a file that is not open."); std::string magic; int h, w, max; @@ -494,6 +546,5 @@ int main(int argc, char** argv) { return sample::gLogger.reportFail(sampleTest); } - return sample::gLogger.reportPass(sampleTest); } diff --git a/samples/opensource/sampleFasterRCNN/sampleFasterRCNN.cpp b/samples/opensource/sampleFasterRCNN/sampleFasterRCNN.cpp index 4ad69471..0c29a83d 100644 --- a/samples/opensource/sampleFasterRCNN/sampleFasterRCNN.cpp +++ b/samples/opensource/sampleFasterRCNN/sampleFasterRCNN.cpp @@ -36,6 +36,8 @@ #include #include +using samplesCommon::SampleUniquePtr; + const std::string gSampleName = "TensorRT.sample_fasterRCNN"; //! @@ -54,9 +56,6 @@ struct SampleFasterRCNNParams : public samplesCommon::CaffeSampleParams //! class SampleFasterRCNN { - template - using SampleUniquePtr = std::unique_ptr; - public: SampleFasterRCNN(const SampleFasterRCNNParams& params) : mParams(params) @@ -137,7 +136,7 @@ bool SampleFasterRCNN::build() return false; } - auto network = SampleUniquePtr(builder->createNetwork()); + auto network = SampleUniquePtr(builder->createNetworkV2(0)); if (!network) { return false; @@ -154,18 +153,39 @@ bool SampleFasterRCNN::build() { return false; } + + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { + return false; + } + config->setProfileStream(*profileStream); + constructNetwork(parser, builder, network, config); + SampleUniquePtr plan{builder->buildSerializedNetwork(*network, *config)}; + if (!plan) + { + return false; + } + + SampleUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + if (!runtime) + { + return false; + } + mEngine = std::shared_ptr( - builder->buildEngineWithConfig(*network, *config), samplesCommon::InferDeleter()); + runtime->deserializeCudaEngine(plan->data(), plan->size()), samplesCommon::InferDeleter()); if (!mEngine) { return false; } - assert(network->getNbInputs() == 2); + ASSERT(network->getNbInputs() == 2); mInputDims = network->getInput(0)->getDimensions(); - assert(mInputDims.nbDims == 3); + ASSERT(mInputDims.nbDims == 3); return true; } @@ -214,7 +234,7 @@ bool SampleFasterRCNN::infer() } // Read the input data into the managed buffers - assert(mParams.inputTensorNames.size() == 2); + ASSERT(mParams.inputTensorNames.size() == 2); if (!processInput(buffers)) { return false; @@ -266,7 +286,7 @@ bool SampleFasterRCNN::processInput(const samplesCommon::BufferManager& buffers) // Available images const std::vector imageList = {"000456.ppm", "000542.ppm", "001150.ppm", "001763.ppm", "004545.ppm"}; mPPMs.resize(batchSize); - assert(mPPMs.size() <= imageList.size()); + ASSERT(mPPMs.size() <= imageList.size()); // Fill im_info buffer float* hostImInfoBuffer = static_cast(buffers.getHostBuffer("im_info")); diff --git a/samples/opensource/sampleGoogleNet/sampleGoogleNet.cpp b/samples/opensource/sampleGoogleNet/sampleGoogleNet.cpp index abd62c95..ba25a0f6 100644 --- a/samples/opensource/sampleGoogleNet/sampleGoogleNet.cpp +++ b/samples/opensource/sampleGoogleNet/sampleGoogleNet.cpp @@ -36,6 +36,8 @@ #include #include +using samplesCommon::SampleUniquePtr; + const std::string gSampleName = "TensorRT.sample_googlenet"; //! @@ -45,9 +47,6 @@ const std::string gSampleName = "TensorRT.sample_googlenet"; //! class SampleGoogleNet { - template - using SampleUniquePtr = std::unique_ptr; - public: SampleGoogleNet(const samplesCommon::CaffeSampleParams& params) : mParams(params) @@ -97,7 +96,7 @@ bool SampleGoogleNet::build() return false; } - auto network = SampleUniquePtr(builder->createNetwork()); + auto network = SampleUniquePtr(builder->createNetworkV2(0)); if (!network) { return false; @@ -120,10 +119,32 @@ bool SampleGoogleNet::build() config->setMaxWorkspaceSize(16_MiB); samplesCommon::enableDLA(builder.get(), config.get(), mParams.dlaCore); - mEngine = std::shared_ptr( - builder->buildEngineWithConfig(*network, *config), samplesCommon::InferDeleter()); - if (!mEngine) + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { return false; + } + config->setProfileStream(*profileStream); + + SampleUniquePtr plan{builder->buildSerializedNetwork(*network, *config)}; + if (!plan) + { + return false; + } + + SampleUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + if (!runtime) + { + return false; + } + + mEngine = std::shared_ptr( + runtime->deserializeCudaEngine(plan->data(), plan->size()), samplesCommon::InferDeleter()); + if (!mEngine) + { + return false; + } return true; } diff --git a/samples/opensource/sampleINT8/sampleINT8.cpp b/samples/opensource/sampleINT8/sampleINT8.cpp index 5d9e13b3..0429d9f6 100644 --- a/samples/opensource/sampleINT8/sampleINT8.cpp +++ b/samples/opensource/sampleINT8/sampleINT8.cpp @@ -38,6 +38,8 @@ #include #include +using samplesCommon::SampleUniquePtr; + const std::string gSampleName = "TensorRT.sample_int8"; //! @@ -57,9 +59,6 @@ struct SampleINT8Params : public samplesCommon::CaffeSampleParams //! class SampleINT8 { - template - using SampleUniquePtr = std::unique_ptr; - public: SampleINT8(const SampleINT8Params& params) : mParams(params) @@ -133,7 +132,13 @@ bool SampleINT8::build(DataType dataType) return false; } - auto network = SampleUniquePtr(builder->createNetwork()); + if ((dataType == DataType::kINT8 && !builder->platformHasFastInt8()) + || (dataType == DataType::kHALF && !builder->platformHasFastFp16())) + { + return false; + } + + auto network = SampleUniquePtr(builder->createNetworkV2(0)); if (!network) { return false; @@ -151,21 +156,15 @@ bool SampleINT8::build(DataType dataType) return false; } - if ((dataType == DataType::kINT8 && !builder->platformHasFastInt8()) - || (dataType == DataType::kHALF && !builder->platformHasFastFp16())) - { - return false; - } - auto constructed = constructNetwork(builder, network, config, parser, dataType); if (!constructed) { return false; } - assert(network->getNbInputs() == 1); + ASSERT(network->getNbInputs() == 1); mInputDims = network->getInput(0)->getDimensions(); - assert(mInputDims.nbDims == 3); + ASSERT(mInputDims.nbDims == 3); return true; } @@ -252,8 +251,28 @@ bool SampleINT8::constructNetwork(SampleUniquePtr& builder, } } + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { + return false; + } + config->setProfileStream(*profileStream); + + SampleUniquePtr plan{builder->buildSerializedNetwork(*network, *config)}; + if (!plan) + { + return false; + } + + SampleUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + if (!runtime) + { + return false; + } + mEngine = std::shared_ptr( - builder->buildEngineWithConfig(*network, *config), samplesCommon::InferDeleter()); + runtime->deserializeCudaEngine(plan->data(), plan->size()), samplesCommon::InferDeleter()); if (!mEngine) { return false; @@ -287,14 +306,14 @@ bool SampleINT8::infer(std::vector& score, int firstScoreBatch, int nbSco Dims outputDims = context->getEngine().getBindingDimensions( context->getEngine().getBindingIndex(mParams.outputTensorNames[0].c_str())); - int outputSize = samplesCommon::volume(outputDims); + int64_t outputSize = samplesCommon::volume(outputDims); int top1{0}, top5{0}; float totalTime{0.0f}; while (batchStream.next()) { // Read the input data into the managed buffers - assert(mParams.inputTensorNames.size() == 1); + ASSERT(mParams.inputTensorNames.size() == 1); if (!processInput(buffers, batchStream.getBatch())) { return false; diff --git a/samples/opensource/sampleINT8API/sampleINT8API.cpp b/samples/opensource/sampleINT8API/sampleINT8API.cpp index 26cd0542..8c9b0bf0 100644 --- a/samples/opensource/sampleINT8API/sampleINT8API.cpp +++ b/samples/opensource/sampleINT8API/sampleINT8API.cpp @@ -17,7 +17,7 @@ //! sampleINT8API.cpp //! This file contains implementation showcasing usage of INT8 calibration and precision APIs. //! It creates classification networks such as mobilenet, vgg19, resnet-50 from onnx model file. -//! This sample showcae setting per tensor dynamic range overriding calibrator generated scales if it exists. +//! This sample showcae setting per-tensor dynamic range overriding calibrator generated scales if it exists. //! This sample showcase how to set computation precision of layer. It involves forcing output tensor type of the layer //! to particular precision. It can be run with the following command line: Command: ./sample_int8_api [-h or --help] //! [-m modelfile] [-s per_tensor_dynamic_range_file] [-i image_file] [-r reference_file] [-d path/to/data/dir] @@ -39,6 +39,8 @@ #include #include +using samplesCommon::SampleUniquePtr; + const std::string gSampleName = "TensorRT.sample_int8_api"; struct SampleINT8APIPreprocessing @@ -127,7 +129,7 @@ private: bool verifyOutput(const samplesCommon::BufferManager& buffers) const; //! - //! \brief Populate per tensor dynamic range values + //! \brief Populate per-tensor dynamic range values //! bool readPerTensorDynamicRangeValues(); @@ -153,7 +155,7 @@ private: void SampleINT8API::getInputOutputNames() { int nbindings = mEngine.get()->getNbBindings(); - assert(nbindings == 2); + ASSERT(nbindings == 2); for (int b = 0; b < nbindings; ++b) { nvinfer1::Dims dims = mEngine.get()->getBindingDimensions(b); @@ -179,14 +181,14 @@ void SampleINT8API::getInputOutputNames() } //! -//! \brief Populate per tensor dyanamic range values +//! \brief Populate per-tensor dyanamic range values //! bool SampleINT8API::readPerTensorDynamicRangeValues() { std::ifstream iDynamicRangeStream(mParams.dynamicRangeFileName); if (!iDynamicRangeStream) { - sample::gLogError << "Could not find per tensor scales file: " << mParams.dynamicRangeFileName << std::endl; + sample::gLogError << "Could not find per-tensor scales file: " << mParams.dynamicRangeFileName << std::endl; return false; } @@ -251,11 +253,10 @@ void SampleINT8API::setLayerPrecision(SampleUniquePtr& network) { - sample::gLogInfo << "Sample requires to run with per tensor dynamic range." << std::endl; - sample::gLogInfo - << "In order to run Int8 inference without calibration, user will need to provide dynamic range for all " - "the network tensors." - << std::endl; + sample::gLogInfo << "Sample requires to run with per-tensor dynamic range." << std::endl; + sample::gLogInfo << "In order to run Int8 inference without calibration, user will need to provide dynamic range for all " + "the network tensors." + << std::endl; std::ofstream tensorsFile{mParams.networkTensorsFileName}; @@ -298,7 +299,7 @@ void SampleINT8API::writeNetworkTensorNames(const SampleUniquePtr& network) { - // populate per tensor dynamic range + // populate per-tensor dynamic range if (!readPerTensorDynamicRangeValues()) { return false; @@ -307,14 +308,12 @@ bool SampleINT8API::setDynamicRange(SampleUniquePtrgetNbInputs(); ++i) @@ -422,7 +421,7 @@ bool SampleINT8API::prepareInput(const samplesCommon::BufferManager& buffers) std::vector fileData(channels * height * width); std::ifstream infile(mParams.imageFileName, std::ifstream::binary); - assert(infile.is_open() && "Attempting to read from a file that is not open."); + ASSERT(infile.is_open() && "Attempting to read from a file that is not open."); infile >> magic >> width >> height >> max; infile.seekg(1, infile.cur); infile.read(reinterpret_cast(fileData.data()), width * height * channels); @@ -496,6 +495,12 @@ sample::Logger::TestResult SampleINT8API::build() return sample::Logger::TestResult::kFAILED; } + if (!builder->platformHasFastInt8()) + { + sample::gLogError << "Platform does not support INT8 inference. sampleINT8API can only run in INT8 Mode." << std::endl; + return sample::Logger::TestResult::kWAIVED; + } + const auto explicitBatch = 1U << static_cast(NetworkDefinitionCreationFlag::kEXPLICIT_BATCH); auto network = SampleUniquePtr(builder->createNetworkV2(explicitBatch)); if (!network) @@ -533,18 +538,11 @@ sample::Logger::TestResult SampleINT8API::build() return sample::Logger::TestResult::kWAIVED; } - if (!builder->platformHasFastInt8()) - { - sample::gLogError << "Platform does not support INT8 inference. sampleINT8API can only run in INT8 Mode." - << std::endl; - return sample::Logger::TestResult::kWAIVED; - } - // Configure buider config->setFlag(BuilderFlag::kGPU_FALLBACK); config->setMaxWorkspaceSize(1_GiB); - // Enable INT8 model. Required to set custom per tensor dynamic range or INT8 Calibration + // Enable INT8 model. Required to set custom per-tensor dynamic range or INT8 Calibration config->setFlag(BuilderFlag::kINT8); // Mark calibrator as null. As user provides dynamic range for each tensor, no calibrator is required config->setInt8Calibrator(nullptr); @@ -555,13 +553,35 @@ sample::Logger::TestResult SampleINT8API::build() // set INT8 Per Tensor Dynamic range if (!setDynamicRange(network)) { - sample::gLogError << "Unable to set per tensor dynamic range." << std::endl; + sample::gLogError << "Unable to set per-tensor dynamic range." << std::endl; + return sample::Logger::TestResult::kFAILED; + } + + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { + return sample::Logger::TestResult::kFAILED; + } + config->setProfileStream(*profileStream); + + SampleUniquePtr plan{builder->buildSerializedNetwork(*network, *config)}; + if (!plan) + { + sample::gLogError << "Unable to build serialized plan." << std::endl; + return sample::Logger::TestResult::kFAILED; + } + + SampleUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + if (!runtime) + { + sample::gLogError << "Unable to create runtime." << std::endl; return sample::Logger::TestResult::kFAILED; } // build TRT engine mEngine = std::shared_ptr( - builder->buildEngineWithConfig(*network, *config), samplesCommon::InferDeleter()); + runtime->deserializeCudaEngine(plan->data(), plan->size()), samplesCommon::InferDeleter()); if (!mEngine) { sample::gLogError << "Unable to build cuda engine." << std::endl; @@ -789,7 +809,7 @@ void printHelpInfo() std::cout << "--reference=reference.txt or /absolute/path/to/reference.txt. Reference labels file. Defaults to " "reference_labels.txt" << std::endl; - std::cout << "--ranges=ranges.txt or /absolute/path/to/ranges.txt. Specify custom per tensor dynamic range for the " + std::cout << "--ranges=ranges.txt or /absolute/path/to/ranges.txt. Specify custom per-tensor dynamic range for the " "network. Defaults to resnet50_per_tensor_dynamic_range.txt" << std::endl; std::cout << "--write_tensors. Option to generate file containing network tensors name. By default writes to " @@ -807,7 +827,7 @@ void printHelpInfo() std::cout << "--useDLACore=N. Specify a DLA engine for layers that support DLA. Value can range from 0 to n-1, " "where n is the number of DLA engines on the platform." << std::endl; - std::cout << "--verbose. Outputs per tensor dynamic range and layer precision info for the network" << std::endl; + std::cout << "--verbose. Outputs per-tensor dynamic range and layer precision info for the network" << std::endl; } int main(int argc, char** argv) diff --git a/samples/opensource/sampleMLP/convert_weights.py b/samples/opensource/sampleMLP/convert_weights.py index f0adf7be..42b8f78a 100644 --- a/samples/opensource/sampleMLP/convert_weights.py +++ b/samples/opensource/sampleMLP/convert_weights.py @@ -36,9 +36,9 @@ parser.add_argument('-o', '--output', required=True, help='The weight file to du opt = parser.parse_args() -print("Outputting the trained weights in TensorRT's wts v2 format. This format is documented as:") -print("Line 0: ") -print("Line 1-Num: [buffer name] [buffer type] [(buffer shape{e.g. (1, 2, 3)}] ") +print ("Outputting the trained weights in TensorRT's wts v2 format. This format is documented as:") +print ("Line 0: ") +print ("Line 1-Num: [buffer name] [buffer type] [(buffer shape{e.g. (1, 2, 3)}] ") inputbase = opt.model outputbase = opt.output diff --git a/samples/opensource/sampleMLP/sampleMLP.cpp b/samples/opensource/sampleMLP/sampleMLP.cpp index 15a4b4e9..e2a92170 100644 --- a/samples/opensource/sampleMLP/sampleMLP.cpp +++ b/samples/opensource/sampleMLP/sampleMLP.cpp @@ -36,6 +36,8 @@ #include #include +using samplesCommon::SampleUniquePtr; + const std::string gSampleName = "TensorRT.sample_mlp"; //! @@ -56,9 +58,6 @@ struct SampleMLPParams : public samplesCommon::SampleParams //! class SampleMLP { - template - using SampleUniquePtr = std::unique_ptr; - public: SampleMLP(const SampleMLPParams& params) : mParams(params) @@ -91,7 +90,7 @@ private: std::shared_ptr mEngine; //!< The TensorRT engine used to run the network - std::vector> weightsMemory; //!< Host weights memory holder + std::vector> weightsMemory; //!< Host weights memory holder //! //! \brief Uses the API to create the MLP Network @@ -149,7 +148,7 @@ bool SampleMLP::build() return false; } - auto network = SampleUniquePtr(builder->createNetwork()); + auto network = SampleUniquePtr(builder->createNetworkV2(0)); if (!network) { return false; @@ -167,13 +166,13 @@ bool SampleMLP::build() return false; } - assert(network->getNbInputs() == 1); + ASSERT(network->getNbInputs() == 1); auto inputDims = network->getInput(0)->getDimensions(); - assert(inputDims.nbDims == 3); + ASSERT(inputDims.nbDims == 3); - assert(network->getNbOutputs() == 1); + ASSERT(network->getNbOutputs() == 1); auto outputDims = network->getOutput(0)->getDimensions(); - assert(outputDims.nbDims == 3); + ASSERT(outputDims.nbDims == 3); return true; } @@ -192,7 +191,7 @@ bool SampleMLP::constructNetwork(SampleUniquePtr& builder, // Currently the mnist example is only trained in FP32 mode. auto input = network->addInput(mParams.inputTensorNames[0].c_str(), nvinfer1::DataType::kFLOAT, nvinfer1::Dims3{(mParams.inputH * mParams.inputW), 1, 1}); - assert(input != nullptr); + ASSERT(input != nullptr); for (int i = 0; i < 2; ++i) { @@ -210,10 +209,10 @@ bool SampleMLP::constructNetwork(SampleUniquePtr& builder, auto finalLayer = addMLPLayer(network.get(), *input, mParams.outputSize, mWeightMap["outputWeights"].second, mWeightMap["outputBias"].second, nvinfer1::ActivationType::kSIGMOID, -1); - assert(finalLayer != nullptr); + ASSERT(finalLayer != nullptr); // Run topK to get the final result auto topK = network->addTopK(*finalLayer->getOutput(0), nvinfer1::TopKOperation::kMAX, 1, 0x1); - assert(topK != nullptr); + ASSERT(topK != nullptr); topK->setName("OutputTopK"); topK->getOutput(1)->setName(mParams.outputTensorNames[0].c_str()); network->markOutput(*topK->getOutput(1)); @@ -222,8 +221,6 @@ bool SampleMLP::constructNetwork(SampleUniquePtr& builder, // Build engine builder->setMaxBatchSize(mParams.batchSize); config->setMaxWorkspaceSize(16_MiB); - builder->setFp16Mode(mParams.fp16); - builder->setInt8Mode(mParams.int8); if (mParams.fp16) { config->setFlag(BuilderFlag::kFP16); @@ -231,13 +228,37 @@ bool SampleMLP::constructNetwork(SampleUniquePtr& builder, if (mParams.int8) { config->setFlag(BuilderFlag::kINT8); - samplesCommon::setAllTensorScales(network.get(), 64.0f, 64.0f); + samplesCommon::setAllDynamicRanges(network.get(), 64.0f, 64.0f); } samplesCommon::enableDLA(builder.get(), config.get(), mParams.dlaCore); + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { + return false; + } + config->setProfileStream(*profileStream); + + SampleUniquePtr plan{builder->buildSerializedNetwork(*network, *config)}; + if (!plan) + { + return false; + } + + SampleUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + if (!runtime) + { + return false; + } + mEngine = std::shared_ptr( - builder->buildEngineWithConfig(*network, *config), samplesCommon::InferDeleter()); + runtime->deserializeCudaEngine(plan->data(), plan->size()), samplesCommon::InferDeleter()); + if (!mEngine) + { + return false; + } if (!mEngine) { return false; @@ -264,7 +285,7 @@ bool SampleMLP::infer() } // Read the input data into the managed buffers - assert(mParams.inputTensorNames.size() == 1); + ASSERT(mParams.inputTensorNames.size() == 1); if (!processInput(buffers)) { return false; @@ -363,10 +384,10 @@ std::map> SampleMLP::l { std::map> weightMap; std::ifstream input(file, std::ios_base::binary); - assert(input.is_open() && "Unable to load weight file."); + ASSERT(input.is_open() && "Unable to load weight file."); int32_t count; input >> count; - assert(count > 0 && "Invalid weight map file."); + ASSERT(count > 0 && "Invalid weight map file."); while (count--) { std::pair wt{}; @@ -376,12 +397,12 @@ std::map> SampleMLP::l wt.first = loadShape(input); wt.second.type = static_cast(type); wt.second.count = std::accumulate(wt.first.d, wt.first.d + wt.first.nbDims, 1, std::multiplies()); - assert(wt.second.type == nvinfer1::DataType::kFLOAT); + ASSERT(wt.second.type == nvinfer1::DataType::kFLOAT); weightsMemory.emplace_back(new samplesCommon::FloatMemory(wt.second.count)); auto value = weightsMemory.back()->data(); input.read(static_cast(value), wt.second.count * sizeof(float)); - assert(input.peek() == '\n'); + ASSERT(input.peek() == '\n'); // Consume the newline at the end of the data blob. input.get(); wt.second.values = value; @@ -406,15 +427,15 @@ nvinfer1::Dims SampleMLP::loadShape(std::ifstream& input) input >> tmp; shapeStr += tmp; } while (*shapeStr.rbegin() != ')'); - assert(input.peek() == ' '); + ASSERT(input.peek() == ' '); // Consume the space between the shape and the data buffer. input.get(); // Convert to "A,B,C,...,Y[,]" - assert(*shapeStr.begin() == '('); + ASSERT(*shapeStr.begin() == '('); shapeStr.erase(0, 1); // - assert(*shapeStr.rbegin() == ')'); + ASSERT(*shapeStr.rbegin() == ')'); shapeStr.pop_back(); // Convert to "A,B,C,...,Y" @@ -447,9 +468,9 @@ nvinfer1::Dims SampleMLP::loadShape(std::ifstream& input) } // Convert to {A, B, C,...,Y} - assert(shapeDim.size() <= shape.MAX_DIMS); - assert(shapeDim.size() > 0); - assert(shape.nbDims == 0); + ASSERT(shapeDim.size() <= shape.MAX_DIMS); + ASSERT(shapeDim.size() > 0); + ASSERT(shape.nbDims == 0); std::for_each( shapeDim.begin(), shapeDim.end(), [&](std::string& val) { shape.d[shape.nbDims++] = std::stoi(val); }); return shape; @@ -492,11 +513,11 @@ nvinfer1::ILayer* SampleMLP::addMLPLayer(nvinfer1::INetworkDefinition* network, { std::string baseName("MLP Layer" + (idx == -1 ? "Output" : std::to_string(idx))); auto fc = network->addFullyConnected(inputTensor, hiddenSize, wts, bias); - assert(fc != nullptr); + ASSERT(fc != nullptr); std::string fcName = baseName + "FullyConnected"; fc->setName(fcName.c_str()); auto act = network->addActivation(*fc->getOutput(0), actType); - assert(act != nullptr); + ASSERT(act != nullptr); std::string actName = baseName + "Activation"; act->setName(actName.c_str()); return act; diff --git a/samples/opensource/sampleMNIST/sampleMNIST.cpp b/samples/opensource/sampleMNIST/sampleMNIST.cpp index 9471cf85..98882c69 100644 --- a/samples/opensource/sampleMNIST/sampleMNIST.cpp +++ b/samples/opensource/sampleMNIST/sampleMNIST.cpp @@ -31,13 +31,14 @@ #include "NvInfer.h" #include -#include #include #include #include #include #include +using samplesCommon::SampleUniquePtr; + const std::string gSampleName = "TensorRT.sample_mnist"; //! @@ -47,9 +48,6 @@ const std::string gSampleName = "TensorRT.sample_mnist"; //! class SampleMNIST { - template - using SampleUniquePtr = std::unique_ptr; - public: SampleMNIST(const samplesCommon::CaffeSampleParams& params) : mParams(params) @@ -117,7 +115,7 @@ bool SampleMNIST::build() return false; } - auto network = SampleUniquePtr(builder->createNetwork()); + auto network = SampleUniquePtr(builder->createNetworkV2(0)); if (!network) { return false; @@ -155,15 +153,36 @@ bool SampleMNIST::build() samplesCommon::enableDLA(builder.get(), config.get(), mParams.dlaCore); - mEngine = std::shared_ptr( - builder->buildEngineWithConfig(*network, *config), samplesCommon::InferDeleter()); - - if (!mEngine) + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { return false; + } + config->setProfileStream(*profileStream); - assert(network->getNbInputs() == 1); + SampleUniquePtr plan{builder->buildSerializedNetwork(*network, *config)}; + if (!plan) + { + return false; + } + + SampleUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + if (!runtime) + { + return false; + } + + mEngine = std::shared_ptr( + runtime->deserializeCudaEngine(plan->data(), plan->size()), samplesCommon::InferDeleter()); + if (!mEngine) + { + return false; + } + + ASSERT(network->getNbInputs() == 1); mInputDims = network->getInput(0)->getDimensions(); - assert(mInputDims.nbDims == 3); + ASSERT(mInputDims.nbDims == 3); return true; } @@ -277,7 +296,7 @@ bool SampleMNIST::constructNetwork( return false; } network->getLayer(0)->setInput(0, *meanSub->getOutput(0)); - samplesCommon::setAllTensorScales(network.get(), 127.0f, 127.0f); + samplesCommon::setAllDynamicRanges(network.get(), 127.0f, 127.0f); return true; } @@ -305,7 +324,7 @@ bool SampleMNIST::infer() // Read the input data into the managed buffers // There should be just 1 input tensor - assert(mParams.inputTensorNames.size() == 1); + ASSERT(mParams.inputTensorNames.size() == 1); if (!processInput(buffers, mParams.inputTensorNames[0], digit)) { return false; @@ -333,7 +352,7 @@ bool SampleMNIST::infer() // Check and print the output of the inference // There should be just one output tensor - assert(mParams.outputTensorNames.size() == 1); + ASSERT(mParams.outputTensorNames.size() == 1); bool outputCorrect = verifyOutput(buffers, mParams.outputTensorNames[0], digit); return outputCorrect; diff --git a/samples/opensource/sampleMNISTAPI/sampleMNISTAPI.cpp b/samples/opensource/sampleMNISTAPI/sampleMNISTAPI.cpp index 9c8fb29c..d8ee3e1e 100644 --- a/samples/opensource/sampleMNISTAPI/sampleMNISTAPI.cpp +++ b/samples/opensource/sampleMNISTAPI/sampleMNISTAPI.cpp @@ -37,6 +37,8 @@ #include #include +using samplesCommon::SampleUniquePtr; + const std::string gSampleName = "TensorRT.sample_mnist_api"; //! @@ -58,9 +60,6 @@ struct SampleMNISTAPIParams : public samplesCommon::SampleParams //! class SampleMNISTAPI { - template - using SampleUniquePtr = std::unique_ptr; - public: SampleMNISTAPI(const SampleMNISTAPIParams& params) : mParams(params) @@ -90,7 +89,7 @@ private: std::map mWeightMap; //!< The weight name to weight value map - std::vector> weightsMemory; //!< Host weights memory holder + std::vector> weightsMemory; //!< Host weights memory holder std::shared_ptr mEngine; //!< The TensorRT engine used to run the network @@ -134,7 +133,7 @@ bool SampleMNISTAPI::build() return false; } - auto network = SampleUniquePtr(builder->createNetwork()); + auto network = SampleUniquePtr(builder->createNetworkV2(0)); if (!network) { return false; @@ -152,13 +151,13 @@ bool SampleMNISTAPI::build() return false; } - assert(network->getNbInputs() == 1); + ASSERT(network->getNbInputs() == 1); auto inputDims = network->getInput(0)->getDimensions(); - assert(inputDims.nbDims == 3); + ASSERT(inputDims.nbDims == 3); - assert(network->getNbOutputs() == 1); + ASSERT(network->getNbOutputs() == 1); auto outputDims = network->getOutput(0)->getDimensions(); - assert(outputDims.nbDims == 3); + ASSERT(outputDims.nbDims == 3); return true; } @@ -176,7 +175,7 @@ bool SampleMNISTAPI::constructNetwork(SampleUniquePtr& build // Create input tensor of shape { 1, 1, 28, 28 } ITensor* data = network->addInput( mParams.inputTensorNames[0].c_str(), DataType::kFLOAT, Dims3{1, mParams.inputH, mParams.inputW}); - assert(data); + ASSERT(data); // Create scale layer with default power/shift and specified scale parameter. const float scaleParam = 0.0125f; @@ -184,47 +183,47 @@ bool SampleMNISTAPI::constructNetwork(SampleUniquePtr& build const Weights shift{DataType::kFLOAT, nullptr, 0}; const Weights scale{DataType::kFLOAT, &scaleParam, 1}; IScaleLayer* scale_1 = network->addScale(*data, ScaleMode::kUNIFORM, shift, scale, power); - assert(scale_1); + ASSERT(scale_1); // Add convolution layer with 20 outputs and a 5x5 filter. IConvolutionLayer* conv1 = network->addConvolutionNd( - *scale_1->getOutput(0), 20, Dims{2, {5, 5}, {}}, mWeightMap["conv1filter"], mWeightMap["conv1bias"]); - assert(conv1); + *scale_1->getOutput(0), 20, Dims{2, {5, 5}}, mWeightMap["conv1filter"], mWeightMap["conv1bias"]); + ASSERT(conv1); conv1->setStride(DimsHW{1, 1}); // Add max pooling layer with stride of 2x2 and kernel size of 2x2. - IPoolingLayer* pool1 = network->addPoolingNd(*conv1->getOutput(0), PoolingType::kMAX, Dims{2, {2, 2}, {}}); - assert(pool1); + IPoolingLayer* pool1 = network->addPoolingNd(*conv1->getOutput(0), PoolingType::kMAX, Dims{2, {2, 2}}); + ASSERT(pool1); pool1->setStride(DimsHW{2, 2}); // Add second convolution layer with 50 outputs and a 5x5 filter. IConvolutionLayer* conv2 = network->addConvolutionNd( - *pool1->getOutput(0), 50, Dims{2, {5, 5}, {}}, mWeightMap["conv2filter"], mWeightMap["conv2bias"]); - assert(conv2); + *pool1->getOutput(0), 50, Dims{2, {5, 5}}, mWeightMap["conv2filter"], mWeightMap["conv2bias"]); + ASSERT(conv2); conv2->setStride(DimsHW{1, 1}); // Add second max pooling layer with stride of 2x2 and kernel size of 2x3> - IPoolingLayer* pool2 = network->addPoolingNd(*conv2->getOutput(0), PoolingType::kMAX, Dims{2, {2, 2}, {}}); - assert(pool2); + IPoolingLayer* pool2 = network->addPoolingNd(*conv2->getOutput(0), PoolingType::kMAX, Dims{2, {2, 2}}); + ASSERT(pool2); pool2->setStride(DimsHW{2, 2}); // Add fully connected layer with 500 outputs. IFullyConnectedLayer* ip1 = network->addFullyConnected(*pool2->getOutput(0), 500, mWeightMap["ip1filter"], mWeightMap["ip1bias"]); - assert(ip1); + ASSERT(ip1); // Add activation layer using the ReLU algorithm. IActivationLayer* relu1 = network->addActivation(*ip1->getOutput(0), ActivationType::kRELU); - assert(relu1); + ASSERT(relu1); // Add second fully connected layer with 20 outputs. IFullyConnectedLayer* ip2 = network->addFullyConnected( *relu1->getOutput(0), mParams.outputSize, mWeightMap["ip2filter"], mWeightMap["ip2bias"]); - assert(ip2); + ASSERT(ip2); // Add softmax layer to determine the probability. ISoftMaxLayer* prob = network->addSoftMax(*ip2->getOutput(0)); - assert(prob); + ASSERT(prob); prob->getOutput(0)->setName(mParams.outputTensorNames[0].c_str()); network->markOutput(*prob->getOutput(0)); @@ -238,13 +237,33 @@ bool SampleMNISTAPI::constructNetwork(SampleUniquePtr& build if (mParams.int8) { config->setFlag(BuilderFlag::kINT8); - samplesCommon::setAllTensorScales(network.get(), 64.0f, 64.0f); + samplesCommon::setAllDynamicRanges(network.get(), 64.0f, 64.0f); } samplesCommon::enableDLA(builder.get(), config.get(), mParams.dlaCore); + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { + return false; + } + config->setProfileStream(*profileStream); + + SampleUniquePtr plan{builder->buildSerializedNetwork(*network, *config)}; + if (!plan) + { + return false; + } + + SampleUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + if (!runtime) + { + return false; + } + mEngine = std::shared_ptr( - builder->buildEngineWithConfig(*network, *config), samplesCommon::InferDeleter()); + runtime->deserializeCudaEngine(plan->data(), plan->size()), samplesCommon::InferDeleter()); if (!mEngine) { return false; @@ -271,7 +290,7 @@ bool SampleMNISTAPI::infer() } // Read the input data into the managed buffers - assert(mParams.inputTensorNames.size() == 1); + ASSERT(mParams.inputTensorNames.size() == 1); if (!processInput(buffers)) { return false; @@ -392,12 +411,12 @@ std::map SampleMNISTAPI::loadWeights(const std:: // Open weights file std::ifstream input(file, std::ios::binary); - assert(input.is_open() && "Unable to load weight file."); + ASSERT(input.is_open() && "Unable to load weight file."); // Read number of weight blobs int32_t count; input >> count; - assert(count > 0 && "Invalid weight map file."); + ASSERT(count > 0 && "Invalid weight map file."); std::map weightMap; while (count--) diff --git a/samples/opensource/sampleMovieLens/README.md b/samples/opensource/sampleMovieLens/README.md deleted file mode 100644 index 2d41c33c..00000000 --- a/samples/opensource/sampleMovieLens/README.md +++ /dev/null @@ -1,189 +0,0 @@ -# Movie Recommendation Using Neural Collaborative Filter (NCF) - - -**Table Of Contents** -- [Description](#description) -- [How does this sample work?](#how-does-this-sample-work) - * [Importing a network to TensorRT](#importing-a-network-to-tensorrt) - * [Running inference](#running-inference) - * [Verifying the output](#verifying-the-output) - * [TensorRT API layers and ops](#tensorrt-api-layers-and-ops) -- [Training an NCF network](#training-an-ncf-network) -- [Preparing sample data](#preparing-sample-data) -- [Running the sample](#running-the-sample) - * [Sample `--help` options](#sample-help-options) -- [Additional resources](#additional-resources) -- [License](#license) -- [Changelog](#changelog) -- [Known issues](#known-issues) - -## Description - -This sample, sampleMovieLens, is an end-to-end sample that imports a trained TensorFlow model and predicts the highest rated movie for each user. This sample demonstrates a simple movie recommender system using a multi-layer perceptron (MLP) based Neural Collaborative Filter (NCF) recommender. - -Specifically, this sample demonstrates how to generate weights for a MovieLens dataset that TensorRT can then accelerate. - -## How does this sample work? - -The network is trained in TensorFlow on the [MovieLens dataset](https://grouplens.org/datasets/movielens/) containing 6,040 users and 3,706 movies. The NCF recommender system is based off of the [Neural Collaborative Filtering](https://arxiv.org/abs/1708.05031) paper. - -Each query to the network consists of a `userID` and list of `MovieIDs`. The network predicts the highest-rated movie for each user. As trained parameters, the network has embeddings for users and movies, and weights for a sequence of MLPs. - -Specifically, this sample: -- [Imports a network to TensorRT](#importing-a-network-to-tensorrt) -- [Runs inference](#running-inference) -- [Verifies the output](#verifying-the-output) - -### Importing a network to TensorRT - -The network is converted from Tensorflow using the UFF converter (see [Converting A Frozen Graph To UFF](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#samplecode3)), and imported using the UFF parser. Constant layers are used to represent the trained parameters within the network, and the MLPs are implemented using MatrixMultiply layers. A TopK operation is added manually after parsing to find the highest rated movie for the given user. - -### Running inference - -The sample fills the input buffer with `userIDs` and their corresponding lists of `MovieIDs`, which are loaded from `movielens_ratings.txt`. Then, it launches the inference to predict the rating probabilities for the movies using TensorRT. - -### Verifying the output - -Finally, the sample compares the outputs predicted by TensorRT with the expected outputs which are given by `movielens_ratings.txt`. For each user, the `MovieID` with the highest probability should match the expected highest-rated `MovieID`. In the verbose mode, the sample also prints out the probability, which should be close to the expected probability. - - -### TensorRT API layers and ops - -In this sample, the following layers are used. For more information about these layers, see the [TensorRT Developer Guide: Layers](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#layers) documentation. - -[Activation layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#activation-layer) -The Activation layer implements element-wise activation functions. - -[MatrixMultiply layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#matrixmultiply-layer) -The MatrixMultiply layer implements matrix multiplication for a collection of matrices. - -[Scale layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#scale-layer) -The Scale layer implements a per-tensor, per-channel, or per-element affine transformation and/or exponentiation by constant values. - -[Shuffle layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#shuffle-layer) -The Shuffle layer implements a reshape and transpose operator for tensors. - -[TopK layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#topk-layer) -The TopK layer finds the top `K` maximum (or minimum) elements along a dimension, returning a reduced tensor and a tensor of index positions. - -## Training an NCF network - -This sample comes with a pre-trained model. However, if you want to train your own model, you would need to also convert the model weights to UFF format before you can run the sample. - -1. Clone the NCF repository. - ```bash - git clone https://github.com/hexiangnan/neural_collaborative_filtering.git - cd neural_collaborative_filtering - git checkout 0cd2681598507f1cc26d110083327069963f4433 - ``` -2. Apply the `sampleMovieLensTraining.patch` file to save the final result. - ```bash - patch -l -p1 < /samples/sampleMovieLens/sampleMovieLensTraining.patch - ``` -3. Train the MLP based NCF network. - ```bash - python3 MLP.py --dataset ml-1m --epochs 20 --batch_size 256 --layers [64,32,16,8] --reg_layers [0,0,0,0] --num_neg 4 --lr 0.001 --learner adam --verbose 1 --out 1 - ``` - - This step produces the following files in the root directory of the Git repo: - - `movielens_ratings.txt`: A text file which contains the lists of `MovieIDs` for each user and the 10 highest-rated `MovieIDs` with their probabilities. - - `sampleMovieLens.pb`: The frozen TensorFlow graph which contains the information of the network structure and parameters. - -4. Convert the trained model weights to UFF format which sampleMovieLens understands. - 1. Convert the `frozen .pb` file to `.uff` format. - ```bash - convert-to-uff sampleMovieLens.pb -p preprocess.py - ``` - - The `preprocess.py` script is a preprocessing step that needs to be applied to the TensorFlow graph before it can be used by TensorRT. The reason for this is that TensorFlow's concatenation operation accounts for the batch dimension while TensorRT's concatenation operation does not. - - The `convert-to-uff` tool is installed together with UFF installation. If you install UFF with deb/rpm, please use the `convert_to_uff.py` script located in `/usr/lib/python3.X/dist-packages/uff*/bin`. - - 2. Copy: - - The `sampleMovieLens.uff` file to the `/data/movielens` directory. - - The `movielens_ratings.txt` file to the `/data/movielens` directory. - - -## Preparing sample data - -1. Download the sample data from [TensorRT release tarball](https://developer.nvidia.com/nvidia-tensorrt-download#), if not already mounted under `/usr/src/tensorrt/data` (NVIDIA NGC containers) and set it to `$TRT_DATADIR`. - ```bash - export TRT_DATADIR=/usr/src/tensorrt/data - ``` - -## Running the sample - -1. Compile the sample by following build instructions in [TensorRT README](https://github.com/NVIDIA/TensorRT/). - -2. Run the sample to predict the highest-rated movie for each user. - ```bash - sample_movielens # Run with default batch=32 i.e. num of users - sample_movielens -b # Run with batch=N i.e. num of users - sample_movielens --verbose # Prints out inputs, outputs, expected outputs, and expected vs predicted probabilities - ``` - -3. Verify that the sample ran successfully. If the sample runs successfully you should see output similar to the following: - ``` - &&&& RUNNING TensorRT.sample_movielens # ./sample_movielens -b 5 - [I] data/movielens/movielens_ratings.txt - [I] Begin parsing model... - [I] End parsing model... - [I] End building engine... - [I] Done execution. Duration : 514.272 microseconds. - [I] Num of users : 5 - [I] Num of Movies : 100 - [I] | User : 0 | Expected Item : 128 | Predicted Item : 128 | - [I] | User : 1 | Expected Item : 133 | Predicted Item : 133 | - [I] | User : 2 | Expected Item : 515 | Predicted Item : 515 | - [I] | User : 3 | Expected Item : 23 | Predicted Item : 23 | - [I] | User : 4 | Expected Item : 134 | Predicted Item : 134 | - &&&& PASSED TensorRT.sample_movielens # ./sample_movielens -b 5 - ``` - - - - This output shows that the sample ran successfully; `PASSED`. - - -### Sample `--help` options - -To see the full list of available options and their descriptions, use the `-h` or `--help` command line option. - - -# Additional resources - -The following resources provide a deeper understanding about sampleMovieLens: - -**MovieLens** -- [MovieLens dataset](https://grouplens.org/datasets/movielens/) -- [Neural Collaborative Filtering Paper](https://arxiv.org/abs/1708.05031) - -**Models** -- [Neural Collaborative Filtering GitHub Repo](https://github.com/hexiangnan/neural_collaborative_filtering) - -**Blogs** -- [Accelerating Recommendation System Inference Performance with TensorRT](https://devblogs.nvidia.com/accelerating-recommendation-system-inference-performance-with-tensorrt/) - -**Videos** -- [SampleMovieLens YouTube Tutorial](https://www.youtube.com/watch?v=r4KG3dehF48) - -**Documentation** -- [Introduction To NVIDIA’s TensorRT Samples](https://docs.nvidia.com/deeplearning/sdk/tensorrt-sample-support-guide/index.html#samples) -- [Jupyter Notebook Tutorial for SampleMovieLens](https://developer.download.nvidia.com/compute/machine-learning/tensorrt/models/sampleMLP-notebook.html?ncid=--47568) -- [Working With TensorRT Using The C++ API](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#c_topics) -- [NVIDIA’s TensorRT Documentation Library](https://docs.nvidia.com/deeplearning/sdk/tensorrt-archived/index.html) - -# 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 - -February 2019 -This `README.md` file was recreated, updated and reviewed. - - -# Known issues - -- Since the UFF converter is not currently supported on Windows, the model cannot be converted to UFF on Windows systems. It is still possible to use the UFF file shipped with the sample. diff --git a/samples/opensource/sampleMovieLens/preprocess.py b/samples/opensource/sampleMovieLens/preprocess.py deleted file mode 100644 index 0758d605..00000000 --- a/samples/opensource/sampleMovieLens/preprocess.py +++ /dev/null @@ -1,23 +0,0 @@ -# -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# 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 graphsurgeon as gs -import tensorflow as tf - -def preprocess(dynamic_graph): - axis = dynamic_graph.find_nodes_by_path("concatenate/concat/axis")[0] - # Set axis to 2, because of discrepancies between TensorFlow and TensorRT. - axis.attr["value"].tensor.int_val[0] = 2 diff --git a/samples/opensource/sampleMovieLens/sampleMovieLens.cpp b/samples/opensource/sampleMovieLens/sampleMovieLens.cpp deleted file mode 100644 index 14ee81af..00000000 --- a/samples/opensource/sampleMovieLens/sampleMovieLens.cpp +++ /dev/null @@ -1,664 +0,0 @@ -/* - * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. - * - * 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. - */ - -//! -//! sampleMovieLens.cpp -//! This file contains the implementation of the MovieLens sample. It creates the network using -//! the MLP NCF Uff model. -//! It can be run with the following command line: -//! Command: ./sample_movielens [-h or --help] [-b NUM_USERS] [--useDLACore=] [--verbose] -//! - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "NvInfer.h" -#include "NvUffParser.h" -#include "argsParser.h" -#include "buffers.h" -#include "common.h" -#include "logger.h" - -const std::string gSampleName = "TensorRT.sample_movielens"; - -// The OutputParams struct holds intermediate/final outputs generated by the MovieLens structure per user. -struct OutputParams -{ - int32_t userId; // The user Id per batch. - int32_t expectedPredictedMaxRatingItem; // The Expected Max Rating Item per user (inference ground truth). - float expectedPredictedMaxRatingItemProb; // The Expected Max Rating Probability. (inference ground truth). - std::vector allItems; // All inferred items per user. - std::vector> itemProbPairVec; // Expected topK items and prob per user. -}; // struct pargs - -//! -//! \brief The SampleMovieLensParams structure groups the additional parameters required by -//! the MovieLens sample. -//! -struct SampleMovieLensParams : public samplesCommon::UffSampleParams -{ - int32_t embeddingVecSize; - int32_t numUsers; // Total number of users. Should be equal to ratings file users count. - int32_t topKMovies; // TopK movies per user. - int32_t numMoviesPerUser; // The number of movies per user. - std::string ratingInputFile; // The input rating file. - bool strict; // Option to run with strict type requirements. - - // The below structures are used to compare the predicted values to inference (ground truth) - std::map> userToItemsMap; // Lookup for inferred items for each user. - std::map>> - userToExpectedItemProbMap; // Lookup for topK items and probs for each user. - std::vector outParamsVec; -}; - -//! -//! \brief The SampleMovieLens class implements the MovieLens sample -//! -//! \details It creates the network using a uff model -//! -class SampleMovieLens -{ - template - using SampleUniquePtr = std::unique_ptr; - -public: - SampleMovieLens(const SampleMovieLensParams& params) - : mParams(params) - { - } - - //! - //! \brief Builds the network engine - //! - bool build(); - - //! - //! \brief Runs the TensorRT inference engine for this sample - //! - bool infer(); - - //! - //! \brief Used to clean up any state created in the sample class - //! - bool teardown(); - -private: - //! - //! \brief Parses a Uff model for a MLP NCF model, creates a TensorRT network, and builds a TensorRT engine. - //! - void constructNetwork(SampleUniquePtr& builder, - SampleUniquePtr& network, SampleUniquePtr& config, - SampleUniquePtr& parser); - //! - //! \brief Copies a batch of input data from SampleMovieLensParams into managed input buffers - //! - bool processInput(const samplesCommon::BufferManager& buffers); - - //! - //! \brief Helper function to read the next line of the MovieLens dataset - //! .csv file and return the contents of the line after the delimeter. - std::string readNextLine(std::ifstream& file, char delim); - - //! - //! \brief Extracts needed dataset values for a single user in the MovieLens, - //! dataset .csv file, and populates the corresponding ground truth data struct - //! - void readInputSample(std::ifstream& file, OutputParams& outParams, std::string line); - - //! - //! \brief Parses the MovieLens dataset and populates the SampleMovieLensParams data structure - //! - void parseMovieLensData(); - - //! - //! \brief Prints the expected recommendation results (ground truth) - //! from the MovieLens dataset for a given user - //! - void printOutputParams(OutputParams& outParams); - - //! - //! \brief Verifies the inference output with ground truth and logs the results - //! - bool verifyOutput( - uint32_t* userInputPtr, uint32_t* /*itemInputPtr*/, uint32_t* topKItemNumberPtr, float* topKItemProbPtr); - - SampleMovieLensParams mParams; - - std::shared_ptr mEngine{nullptr}; //!< The TensorRT engine used to run the network -}; - -//! -//! \brief Creates the network, configures the builder and creates -//! the network engine -//! -//! \details This function creates the MLP NCF network by parsing the Uff model -//! and builds the engine that will be used to generate recommendations (mEngine) -//! -//! \return Returns true if the engine was created successfully and false -//! otherwise -//! -bool SampleMovieLens::build() -{ - auto builder = SampleUniquePtr(nvinfer1::createInferBuilder(sample::gLogger.getTRTLogger())); - if (!builder) - { - return false; - } - auto network = SampleUniquePtr(builder->createNetwork()); - if (!network) - { - return false; - } - auto config = SampleUniquePtr(builder->createBuilderConfig()); - if (!config) - { - return false; - } - auto parser = SampleUniquePtr(nvuffparser::createUffParser()); - if (!parser) - { - return false; - } - - builder->setMaxBatchSize(mParams.batchSize); - config->setMaxWorkspaceSize(1_GiB); - config->setFlag(BuilderFlag::kGPU_FALLBACK); - config->setFlag(BuilderFlag::kSTRICT_TYPES); - if (mParams.fp16) - { - config->setFlag(BuilderFlag::kFP16); - } - samplesCommon::enableDLA(builder.get(), config.get(), mParams.dlaCore); - - constructNetwork(builder, network, config, parser); - - if (!mEngine) - { - return false; - } - - return true; -} - -//! -//! \brief Parses a Uff model for a MLP NCF model, creates a TensorRT network, and builds a TensorRT engine. -//! -void SampleMovieLens::constructNetwork(SampleUniquePtr& builder, - SampleUniquePtr& network, SampleUniquePtr& config, - SampleUniquePtr& parser) -{ - - nvinfer1::Dims inputIndices; - inputIndices.nbDims = 3; - inputIndices.d[0] = mParams.numMoviesPerUser; - inputIndices.d[1] = 1; - inputIndices.d[2] = 1; - - // There should be two input and three output tensors - assert(mParams.inputTensorNames.size() == 2); - assert(mParams.outputTensorNames.size() == 3); - - parser->registerInput(mParams.inputTensorNames[0].c_str(), inputIndices, nvuffparser::UffInputOrder::kNCHW); - parser->registerInput(mParams.inputTensorNames[1].c_str(), inputIndices, nvuffparser::UffInputOrder::kNCHW); - parser->registerOutput(mParams.outputTensorNames[0].c_str()); - - auto dType = mParams.fp16 ? nvinfer1::DataType::kHALF : nvinfer1::DataType::kFLOAT; - sample::gLogInfo << "Begin parsing model..." << std::endl; - - // Parse the uff model to populate the network - if (!parser->parse(mParams.uffFileName.c_str(), *network, dType)) - { - sample::gLogError << "Failure while parsing UFF file" << std::endl; - return; - } - - sample::gLogInfo << "End parsing model..." << std::endl; - - // Add postprocessing i.e. topk layer to the UFF Network - // Retrieve last layer of UFF Network - auto uffLastLayer = network->getLayer(network->getNbLayers() - 1); - - // Reshape output of fully connected layer numOfMovies x 1 x 1 x 1 to numOfMovies x 1 x 1. - auto reshapeLayer = network->addShuffle(*uffLastLayer->getOutput(0)); - reshapeLayer->setReshapeDimensions(nvinfer1::Dims3(1, mParams.numMoviesPerUser, 1)); - assert(reshapeLayer != nullptr); - - // Apply TopK layer to retrieve item probabilities and corresponding index number. - auto topK = network->addTopK(*reshapeLayer->getOutput(0), nvinfer1::TopKOperation::kMAX, mParams.topKMovies, 0x2); - assert(topK != nullptr); - - // Mark outputs for index and probs. Also need to set the item layer type == kINT32. - topK->getOutput(0)->setName(mParams.outputTensorNames[1].c_str()); - topK->getOutput(1)->setName(mParams.outputTensorNames[2].c_str()); - - // Specify topK tensors as outputs - network->markOutput(*topK->getOutput(0)); - network->markOutput(*topK->getOutput(1)); - - // Set the topK indices tensor as INT32 type - topK->getOutput(1)->setType(nvinfer1::DataType::kINT32); - - sample::gLogInfo << "Done constructing network..." << std::endl; - - mEngine = std::shared_ptr( - builder->buildEngineWithConfig(*network, *config), samplesCommon::InferDeleter()); -} - -//! -//! \brief Runs the TensorRT inference engine for this sample -//! -//! \details This function is the main execution function of the sample. It -//! allocates the buffer, sets inputs, executes the engine, and verifies the output. -//! -bool SampleMovieLens::infer() -{ - // Create RAII buffer manager object - samplesCommon::BufferManager buffers(mEngine, mParams.batchSize); - - auto context = SampleUniquePtr(mEngine->createExecutionContext()); - - if (!context) - { - return false; - } - - if (!processInput(buffers)) - { - return false; - } - - // Create CUDA stream for the execution of this inference. - cudaStream_t stream; - CHECK(cudaStreamCreate(&stream)); - - samplesCommon::GpuTimer timer{stream}; - timer.start(); - - // Asynchronously copy data from host input buffers to device input buffers - buffers.copyInputToDeviceAsync(stream); - - // Asynchronously enqueue the inference work - if (!context->enqueue(mParams.batchSize, buffers.getDeviceBindings().data(), stream, nullptr)) - { - return false; - } - - // Asynchronously copy data from device output buffers to host output buffers - buffers.copyOutputToHostAsync(stream); - - // Wait for the work in the stream to complete - cudaStreamSynchronize(stream); - timer.stop(); - sample::gLogInfo << "Done execution. Duration : " << timer.microseconds() << " microseconds." << std::endl; - - // Release stream - cudaStreamDestroy(stream); - - float* topKItemProb = static_cast(buffers.getHostBuffer(mParams.outputTensorNames[1])); - uint32_t* topKItemNumber = static_cast(buffers.getHostBuffer(mParams.outputTensorNames[2])); - - uint32_t* userInput = static_cast(buffers.getHostBuffer(mParams.inputTensorNames[0])); - uint32_t* itemInput = static_cast(buffers.getHostBuffer(mParams.inputTensorNames[1])); - - return SampleMovieLens::verifyOutput(userInput, itemInput, topKItemNumber, topKItemProb); -} - -//! -//! \brief Copies a batch of input data from SampleMovieLensParams into managed input buffers -//! -bool SampleMovieLens::processInput(const samplesCommon::BufferManager& buffers) -{ - // Parse ground truth data and inputs - SampleMovieLens::parseMovieLensData(); - - uint32_t* userInput = static_cast(buffers.getHostBuffer(mParams.inputTensorNames[0])); - uint32_t* itemInput = static_cast(buffers.getHostBuffer(mParams.inputTensorNames[1])); - - // Copy batch of inputs to host buffers - for (int i = 0; i < mParams.batchSize; ++i) - { - for (int k = 0; k < mParams.numMoviesPerUser; ++k) - { - int idx = i * mParams.numMoviesPerUser + k; - userInput[idx] = mParams.outParamsVec[i].userId; - itemInput[idx] = mParams.outParamsVec[i].allItems.at(k); - } - } - - return true; -} - -//! -//! \brief Helper function to read the next line of the MovieLens dataset -//! .csv file and return the contents of the line after the delimeter. -//! -//! \details This function is called from SampleMovieLens::readInputSample() -//! to extract the needed values per user. -std::string SampleMovieLens::readNextLine(std::ifstream& file, char delim) -{ - std::string line; - std::getline(file, line); - auto pos = line.find(delim); - line = line.substr(pos + 1); - return line; -} - -//! -//! \brief Extracts needed dataset values for a single user in the MovieLens, -//! dataset .csv file, and populates the corresponding ground truth data struct -//! -void SampleMovieLens::readInputSample(std::ifstream& file, OutputParams& outParams, std::string line) -{ - // read user name - char delim = ':'; - auto pos = line.find(delim); - line = line.substr(pos + 1); - outParams.userId = std::stoi(line); - // read items - std::string items = readNextLine(file, delim); - items = items.substr(2, items.size() - 2); - std::stringstream ss(items); - std::string i; - while (ss >> i) - { - if (ss.peek() == ',' || ss.peek() == ' ') - { - ss.ignore(); - } - - i = i.substr(0, i.size() - 1); - outParams.allItems.push_back(std::stoi(i)); - } - - // read expected predicted max rating item - outParams.expectedPredictedMaxRatingItem = std::stoi(readNextLine(file, delim)); - - // read expected predicted max rating prob - std::string prob = readNextLine(file, delim); - prob = prob.substr(2, prob.size() - 3); - outParams.expectedPredictedMaxRatingItemProb = std::stof(prob); - - // skip line - std::getline(file, line); - std::getline(file, line); - - // read all the top 10 prediction ratings - for (int i = 0; i < 10; ++i) - { - auto pos = line.find(delim); - int32_t item = std::stoi(line.substr(0, pos - 1)); - float prob = std::stof(line.substr(pos + 2)); - outParams.itemProbPairVec.emplace_back((std::make_pair(item, prob))); - std::getline(file, line); - } -} - -//! -//! \brief Parses the MovieLens dataset and populates the SampleMovieLensParams data structure -//! -void SampleMovieLens::parseMovieLensData() -{ - std::ifstream file; - file.open(mParams.ratingInputFile, std::ios::binary); - std::string line; - int userIdx = 0; - while (std::getline(file, line) && userIdx < mParams.batchSize) - { - OutputParams outParams; - readInputSample(file, outParams, line); - - // store the outParams in the class data structure. - mParams.outParamsVec.push_back(outParams); - - mParams.userToItemsMap[userIdx] = std::move(outParams.allItems); - mParams.userToExpectedItemProbMap[userIdx] = std::move(outParams.itemProbPairVec); - - userIdx++; - printOutputParams(outParams); - } - - // number of users should be equal to number of users in rating file - assert(mParams.batchSize == userIdx); -} - -bool SampleMovieLens::teardown() -{ - nvuffparser::shutdownProtobufLibrary(); - return true; -} - -//! -//! \brief Prints the expected recommendation results (ground truth) -//! from the MovieLens dataset for a given user -//! -void SampleMovieLens::printOutputParams(OutputParams& outParams) -{ - sample::gLogVerbose << "User Id : " << outParams.userId << std::endl; - sample::gLogVerbose << "Expected Predicted Max Rating Item : " << outParams.expectedPredictedMaxRatingItem - << std::endl; - sample::gLogVerbose << "Expected Predicted Max Rating Prob : " << outParams.expectedPredictedMaxRatingItemProb - << std::endl; - sample::gLogVerbose << "Total TopK Items : " << outParams.itemProbPairVec.size() << std::endl; - for (unsigned int i = 0; i < outParams.itemProbPairVec.size(); ++i) - { - sample::gLogVerbose << outParams.itemProbPairVec.at(i).first << " : " << outParams.itemProbPairVec.at(i).second - << std::endl; - } -} - -//! -//! \brief Compares the inference output with ground truth and logs the results -//! -bool SampleMovieLens::verifyOutput( - uint32_t* userInput, uint32_t* /*itemInput*/, uint32_t* topKItemNumber, float* topKItemProb) -{ - bool pass{true}; - - sample::gLogInfo << "Num of users : " << mParams.batchSize << std::endl; - sample::gLogInfo << "Num of Movies : " << mParams.numMoviesPerUser << std::endl; - - sample::gLogVerbose << "|-----------|------------|-----------------|-----------------|" << std::endl; - sample::gLogVerbose << "| User | Item | Expected Prob | Predicted Prob |" << std::endl; - sample::gLogVerbose << "|-----------|------------|-----------------|-----------------|" << std::endl; - - for (int i = 0; i < mParams.batchSize; ++i) - { - int userIdx = userInput[i * mParams.numMoviesPerUser]; - int maxPredictedIdx = topKItemNumber[i * mParams.topKMovies]; - int maxExpectedItem = mParams.userToExpectedItemProbMap.at(userIdx).at(0).first; - int maxPredictedItem = mParams.userToItemsMap.at(userIdx).at(maxPredictedIdx); - pass &= maxExpectedItem == maxPredictedItem; - - for (int k = 0; k < mParams.topKMovies; ++k) - { - int predictedIdx = topKItemNumber[i * mParams.topKMovies + k]; - float predictedProb = topKItemProb[i * mParams.topKMovies + k]; - float expectedProb = mParams.userToExpectedItemProbMap.at(userIdx).at(k).second; - int predictedItem = mParams.userToItemsMap.at(userIdx).at(predictedIdx); - sample::gLogVerbose << "|" << std::setw(10) << userIdx << " | " << std::setw(10) << predictedItem << " | " - << std::setw(15) << expectedProb << " | " << std::setw(15) << predictedProb << " | " - << std::endl; - } - } - - for (int i = 0; i < mParams.batchSize; ++i) - { - int userIdx = userInput[i * mParams.numMoviesPerUser]; - int maxPredictedIdx = topKItemNumber[i * mParams.topKMovies]; - int maxExpectedItem = mParams.userToExpectedItemProbMap.at(userIdx).at(0).first; - int maxPredictedItem = mParams.userToItemsMap.at(userIdx).at(maxPredictedIdx); - sample::gLogInfo << "| User :" << std::setw(4) << userIdx << " | Expected Item :" << std::setw(5) - << maxExpectedItem << " | Predicted Item :" << std::setw(5) << maxPredictedItem << " | " - << std::endl; - } - - return pass; -} - -struct SampleMovieLensArgs -{ - bool help{false}; - int batchSize{32}; - int dlaCore{-1}; - bool fp16{false}; - bool strict{false}; - bool verbose{false}; -}; - -//! -//! \brief Parses the command line arguments for the MovieLens sample, and returns failure -//! if arguments are incorrect -//! -bool parseSampleMovieLensArgs(SampleMovieLensArgs& args, int argc, char* argv[]) -{ - for (int i = 1; i < argc; ++i) - { - std::string argStr(argv[i]); - - if (argStr == "-h" || argStr == "--help") - { - args.help = true; - return true; - } - if (argStr == "-b") - { - i++; - args.batchSize = std::atoi(argv[i]); - } - else if (argStr == "--fp16") - { - args.fp16 = true; - } - else if (argStr == "--strict") - { - args.strict = true; - } - else if (argStr == "--verbose") - { - args.verbose = true; - sample::setReportableSeverity(sample::Logger::Severity::kVERBOSE); - } - else if (argStr.substr(0, 13) == "--useDLACore=" && argStr.size() > 13) - { - args.dlaCore = std::stoi(argv[i] + 13); - } - else - { - return false; - } - } - return true; -} - -//! -//! \brief Initializes members of the params struct using the -//! command line args -//! -SampleMovieLensParams initializeSampleParams(const SampleMovieLensArgs& args) -{ - SampleMovieLensParams params; - - params.dataDirs.push_back("data/movielens/"); - params.dataDirs.push_back("data/samples/movielens/"); - - params.uffFileName = locateFile("sampleMovieLens.uff", params.dataDirs); - params.embeddingVecSize = 32; - params.topKMovies = 1; - params.numMoviesPerUser = 100; - params.ratingInputFile = locateFile("movielens_ratings.txt", params.dataDirs); - - params.inputTensorNames.push_back("user_input"); - params.inputTensorNames.push_back("item_input"); - params.outputTensorNames.push_back("prediction/Sigmoid"); - params.outputTensorNames.push_back("topk_values"); - params.outputTensorNames.push_back("topk_items"); - - params.batchSize = args.batchSize; - params.dlaCore = args.dlaCore; - params.fp16 = args.fp16; - params.strict = args.strict; - - return params; -} - -//! -//! \brief Prints the help information for running this sample -//! -void printHelpInfo() -{ - std::cout << "Usage: ./sample_movielens [-h or --help] [-b NUM_USERS] [--useDLACore=] [--verbose]\n"; - std::cout << "--help Display help information.\n"; - std::cout << "--verbose Enable verbose prints.\n"; - std::cout << "-b NUM_USERS Number of Users i.e. Batch Size (default numUsers==32).\n"; - std::cout << "--useDLACore=N Specify a DLA engine for layers that support " - "DLA. Value can range from 0 to n-1, where n is the number of " - "DLA engines on the platform." - << std::endl; - std::cout << "--fp16 Run in FP16 mode.\n"; - std::cout << "--strict Run with strict type constraints." << std::endl; -} - -int main(int argc, char** argv) -{ - SampleMovieLensArgs args; - bool argsOK = parseSampleMovieLensArgs(args, argc, argv); - if (!argsOK) - { - sample::gLogError << "Invalid arguments" << std::endl; - printHelpInfo(); - return EXIT_FAILURE; - } - if (args.help) - { - printHelpInfo(); - return EXIT_SUCCESS; - } - - auto sampleTest = sample::gLogger.defineTest(gSampleName, argc, argv); - - sample::gLogger.reportTestStart(sampleTest); - - SampleMovieLensParams params = initializeSampleParams(args); - SampleMovieLens sample(params); - - sample::gLogInfo << "Building and running a GPU inference engine for MLP NCF model..." << std::endl; - - if (!sample.build()) - { - return sample::gLogger.reportFail(sampleTest); - } - if (!sample.infer()) - { - return sample::gLogger.reportFail(sampleTest); - } - if (!sample.teardown()) - { - return sample::gLogger.reportFail(sampleTest); - } - - return sample::gLogger.reportPass(sampleTest); -} diff --git a/samples/opensource/sampleMovieLens/sampleMovieLensTraining.patch b/samples/opensource/sampleMovieLens/sampleMovieLensTraining.patch deleted file mode 100644 index 66fb4e8e..00000000 --- a/samples/opensource/sampleMovieLens/sampleMovieLensTraining.patch +++ /dev/null @@ -1,419 +0,0 @@ -Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. - -NOTICE TO LICENSEE: - -This source code and/or documentation ("Licensed Deliverables") are subject to -NVIDIA intellectual property rights under U.S. and international Copyright -laws. - -These Licensed Deliverables contained herein is PROPRIETARY and CONFIDENTIAL -to NVIDIA and is being provided under the terms and conditions of a form of -NVIDIA software license agreement by and between NVIDIA and Licensee ("License -Agreement") or electronically accepted by Licensee. Notwithstanding any terms -or conditions to the contrary in the License Agreement, reproduction or -disclosure of the Licensed Deliverables to any third party without the express -written consent of NVIDIA is prohibited. - -NOTWITHSTANDING ANY TERMS OR CONDITIONS TO THE CONTRARY IN THE LICENSE -AGREEMENT, NVIDIA MAKES NO REPRESENTATION ABOUT THE SUITABILITY OF THESE -LICENSED DELIVERABLES FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS -OR IMPLIED WARRANTY OF ANY KIND. NVIDIA DISCLAIMS ALL WARRANTIES WITH REGARD -TO THESE LICENSED DELIVERABLES, INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE. -NOTWITHSTANDING ANY TERMS OR CONDITIONS TO THE CONTRARY IN THE LICENSE -AGREEMENT, IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, -INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THESE LICENSED DELIVERABLES. - -U.S. Government End Users. These Licensed Deliverables are a "commercial -item" as that term is defined at 48 C.F.R. 2.101 (OCT 1995), consisting of -"commercial computer software" and "commercial computer software -documentation" as such terms are used in 48 C.F.R. 12.212 (SEPT 1995) and is -provided to the U.S. Government only as a commercial end item. Consistent -with 48 C.F.R.12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (JUNE 1995), -all U.S. Government End Users acquire the Licensed Deliverables with only -those rights set forth herein. - -Any use of the Licensed Deliverables in individual and commercial software -must include, in the user documentation and internal comments to the code, the -above Disclaimer and U.S. Government End Users Notice. - -diff --git a/MLP.py b/MLP.py -index 70566c7..93c0d53 100644 ---- a/MLP.py -+++ b/MLP.py -@@ -1,30 +1,27 @@ - ''' - Created on Aug 9, 2016 - Keras Implementation of Multi-Layer Perceptron (GMF) recommender model in: --He Xiangnan et al. Neural Collaborative Filtering. In WWW 2017. -+He Xiangnan et al. Neural Collaborative Filtering. In WWW 2017. - - @author: Xiangnan He (xiangnanhe@gmail.com) - ''' -- -+import shutil - import numpy as np -+import tensorflow as tf - --import theano --import theano.tensor as T --import keras --from keras import backend as K --from keras import initializations --from keras.regularizers import l2, activity_l2 --from keras.models import Sequential, Graph, Model --from keras.layers.core import Dense, Lambda, Activation --from keras.layers import Embedding, Input, Dense, merge, Reshape, Merge, Flatten, Dropout --from keras.constraints import maxnorm --from keras.optimizers import Adagrad, Adam, SGD, RMSprop -+from tensorflow import keras -+from tensorflow.python.keras import initializers -+from tensorflow.python.keras.regularizers import l2 -+from tensorflow.python.keras.models import Model -+from tensorflow.python.keras.layers import Dense, Embedding, Input, Flatten, \ -+ concatenate -+from tensorflow.python.keras.optimizers import Adam, Adagrad, SGD, RMSprop -+from tensorflow.python.framework import graph_util - from evaluate import evaluate_model -+from evaluate import infer_model - from Dataset import Dataset - from time import time --import sys - import argparse --import multiprocessing as mp - - #################### Arguments #################### - def parse_args(): -@@ -54,7 +51,50 @@ def parse_args(): - return parser.parse_args() - - def init_normal(shape, name=None): -- return initializations.normal(shape, scale=0.01, name=name) -+ return initializers.he_normal() -+ -+def freeze_checkpoint_graph(output_node_names, checkpoint_model_folder, output_graph_filename): -+ # retrieve the checkpoint fullpath -+ checkpoint = tf.train.get_checkpoint_state(checkpoint_model_folder) -+ input_checkpoint = checkpoint.model_checkpoint_path -+ -+ print(input_checkpoint) -+ # precise the file fullname of our freezed graph -+ absolute_model_folder = "/".join(input_checkpoint.split("/")[:-1]) -+ -+ # we clear devices, to allow tensorflow to control on the loading, where it wants operations to be calculated -+ clear_devices = True -+ -+ # the checkpoint directory has - .meta and .data i.e. weights file to be retrieved -+ saver = tf.train.import_meta_graph(input_checkpoint + '.meta', clear_devices=clear_devices) -+ -+ # retrieve protobuf graph definition -+ # returns the default graph of the current thread - will be the innermost graph -+ # on which Graph.as_default() context has been entered - global_default_graph if non has been explicitly created -+ graph = tf.get_default_graph() -+ -+ # retrieve graph def for a grpah -+ input_graph_def = graph.as_graph_def() -+ -+ # print the output nodes -+ output_node_list = [n.name for n in tf.get_default_graph().as_graph_def().node] -+ -+ # start the session and restore the weights -+ with tf.Session() as sess: -+ saver.restore(sess, input_checkpoint) -+ -+ # in order to freeze the graph - need to export the variables to constants -+ output_graph_def = graph_util.convert_variables_to_constants( -+ sess, # session have weights stored -+ input_graph_def, -+ output_node_names.split(",") -+ ) -+ -+ # finally we serialize and dump the output graph to the filesystem -+ with tf.gfile.GFile(output_graph_filename, "wb") as f: -+ f.write(output_graph_def.SerializeToString()) -+ -+ print("[FREEZE_INFO] ", len(output_graph_def.node), " ops in the final graph.") - - def get_model(num_users, num_items, layers = [20,10], reg_layers=[0,0]): - assert len(layers) == len(reg_layers) -@@ -63,29 +103,43 @@ def get_model(num_users, num_items, layers = [20,10], reg_layers=[0,0]): - user_input = Input(shape=(1,), dtype='int32', name = 'user_input') - item_input = Input(shape=(1,), dtype='int32', name = 'item_input') - -- MLP_Embedding_User = Embedding(input_dim = num_users, output_dim = layers[0]/2, name = 'user_embedding', -- init = init_normal, W_regularizer = l2(reg_layers[0]), input_length=1) -- MLP_Embedding_Item = Embedding(input_dim = num_items, output_dim = layers[0]/2, name = 'item_embedding', -- init = init_normal, W_regularizer = l2(reg_layers[0]), input_length=1) -- -+ MLP_Embedding_User = Embedding(input_dim=num_users, -+ output_dim=int(layers[0] // 2), -+ name='user_embedding', -+ embeddings_initializer='random_uniform', -+ embeddings_regularizer=l2(reg_layers[0]), -+ input_length=1) -+ MLP_Embedding_Item = Embedding(input_dim=num_items, -+ output_dim=int(layers[0] // 2), -+ name='item_embedding', -+ embeddings_initializer='random_uniform', -+ embeddings_regularizer=l2(reg_layers[0]), -+ input_length=1) - # Crucial to flatten an embedding vector! - user_latent = Flatten()(MLP_Embedding_User(user_input)) - item_latent = Flatten()(MLP_Embedding_Item(item_input)) -- -+ - # The 0-th layer is the concatenation of embedding layers -- vector = merge([user_latent, item_latent], mode = 'concat') -- -+ vector = concatenate([user_latent, item_latent]) -+ - # MLP layers -- for idx in xrange(1, num_layer): -- layer = Dense(layers[idx], W_regularizer= l2(reg_layers[idx]), activation='relu', name = 'layer%d' %idx) -+ for idx in range(1, num_layer): -+ print(idx, " : ", layers[idx]) -+ layer = Dense(layers[idx], -+ kernel_regularizer=l2(reg_layers[idx]), -+ activation='relu', -+ name='layer%d'%idx) - vector = layer(vector) -- -+ - # Final prediction layer -- prediction = Dense(1, activation='sigmoid', init='lecun_uniform', name = 'prediction')(vector) -- -- model = Model(input=[user_input, item_input], -- output=prediction) -- -+ prediction = Dense(1, -+ activation='sigmoid', -+ kernel_initializer='lecun_uniform', -+ name='prediction')(vector) -+ -+ model = Model(inputs=[user_input, item_input], -+ outputs=prediction) -+ - return model - - def get_train_instances(train, num_negatives): -@@ -97,9 +151,10 @@ def get_train_instances(train, num_negatives): - item_input.append(i) - labels.append(1) - # negative instances -- for t in xrange(num_negatives): -+ for t in range(num_negatives): - j = np.random.randint(num_items) -- while train.has_key((u, j)): -+ #while train.has_key((u, j)): -+ while (u, j) in train: - j = np.random.randint(num_items) - user_input.append(u) - item_input.append(j) -@@ -118,61 +173,73 @@ if __name__ == '__main__': - batch_size = args.batch_size - epochs = args.epochs - verbose = args.verbose -- -+ - topK = 10 - evaluation_threads = 1 #mp.cpu_count() - print("MLP arguments: %s " %(args)) -- model_out_file = 'Pretrain/%s_MLP_%s_%d.h5' %(args.dataset, args.layers, time()) -- -+ - # Loading data - t1 = time() - dataset = Dataset(args.path + args.dataset) - train, testRatings, testNegatives = dataset.trainMatrix, dataset.testRatings, dataset.testNegatives - num_users, num_items = train.shape -- print("Load data done [%.1f s]. #user=%d, #item=%d, #train=%d, #test=%d" -+ print("Load data done [%.1f s]. #user=%d, #item=%d, #train=%d, #test=%d" - %(time()-t1, num_users, num_items, train.nnz, len(testRatings))) -- -+ - # Build model - model = get_model(num_users, num_items, layers, reg_layers) -- if learner.lower() == "adagrad": -+ if learner.lower() == "adagrad": - model.compile(optimizer=Adagrad(lr=learning_rate), loss='binary_crossentropy') - elif learner.lower() == "rmsprop": - model.compile(optimizer=RMSprop(lr=learning_rate), loss='binary_crossentropy') - elif learner.lower() == "adam": - model.compile(optimizer=Adam(lr=learning_rate), loss='binary_crossentropy') - else: -- model.compile(optimizer=SGD(lr=learning_rate), loss='binary_crossentropy') -- -+ model.compile(optimizer=SGD(lr=learning_rate), loss='binary_crossentropy') -+ - # Check Init performance - t1 = time() - (hits, ndcgs) = evaluate_model(model, testRatings, testNegatives, topK, evaluation_threads) - hr, ndcg = np.array(hits).mean(), np.array(ndcgs).mean() - print('Init: HR = %.4f, NDCG = %.4f [%.1f]' %(hr, ndcg, time()-t1)) -- -+ -+ saver = tf.train.Saver() -+ - # Train model - best_hr, best_ndcg, best_iter = hr, ndcg, -1 -- for epoch in xrange(epochs): -+ for epoch in range(epochs): -+ print("Training epochs : ", epoch) - t1 = time() - # Generate training instances - user_input, item_input, labels = get_train_instances(train, num_negatives) -- -- # Training -+ -+ # Training - hist = model.fit([np.array(user_input), np.array(item_input)], #input -- np.array(labels), # labels -- batch_size=batch_size, nb_epoch=1, verbose=0, shuffle=True) -+ np.array(labels), # labels -+ batch_size=batch_size, epochs=1, verbose=0, shuffle=True) - t2 = time() - - # Evaluation - if epoch %verbose == 0: - (hits, ndcgs) = evaluate_model(model, testRatings, testNegatives, topK, evaluation_threads) - hr, ndcg, loss = np.array(hits).mean(), np.array(ndcgs).mean(), hist.history['loss'][0] -- print('Iteration %d [%.1f s]: HR = %.4f, NDCG = %.4f, loss = %.4f [%.1f s]' -+ print('Iteration %d [%.1f s]: HR = %.4f, NDCG = %.4f, loss = %.4f [%.1f s]' - % (epoch, t2-t1, hr, ndcg, loss, time()-t2)) - if hr > best_hr: - best_hr, best_ndcg, best_iter = hr, ndcg, epoch -- if args.out > 0: -- model.save_weights(model_out_file, overwrite=True) -+ # Model is trained, all epochs are done, save the golden data -+ infer_model(model, testRatings, testNegatives, topK, evaluation_threads) - - print("End. Best Iteration %d: HR = %.4f, NDCG = %.4f. " %(best_iter, best_hr, best_ndcg)) -- if args.out > 0: -- print("The best MLP model is saved to %s" %(model_out_file)) -+ # Get keras session -+ save_path = saver.save(tf.keras.backend.get_session(), './ckpts/sampleMovieLens.ckpt') -+ -+ output_node_names = "prediction/Sigmoid" -+ checkpoint_model_folder = "./ckpts/"; -+ output_graph_filename = "sampleMovieLens.pb" -+ -+ # convert checkpoints to frozen graph -+ freeze_checkpoint_graph(output_node_names, checkpoint_model_folder, output_graph_filename) -+ -+ # delete checkpoints file -+ shutil.rmtree("./ckpts") -diff --git a/evaluate.py b/evaluate.py -index 729f07a..6079a8a 100644 ---- a/evaluate.py -+++ b/evaluate.py -@@ -20,6 +20,71 @@ _testRatings = None - _testNegatives = None - _K = None - -+def infer_model(model, testRatings, testNegatives, K, num_thread): -+ """ -+ Evaluate the performance (Hit_Ratio, NDCG) of top-K recommendation -+ Return: score of each test rating. -+ """ -+ global _model -+ global _testRatings -+ global _testNegatives -+ global _K -+ _model = model -+ _testRatings = testRatings -+ _testNegatives = testNegatives -+ _K = K -+ -+ hits, ndcgs = [],[] -+ if(num_thread > 1): # Multi-thread -+ pool = multiprocessing.Pool(processes=num_thread) -+ res = pool.map(eval_one_rating, range(len(_testRatings))) -+ pool.close() -+ pool.join() -+ hits = [r[0] for r in res] -+ ndcgs = [r[1] for r in res] -+ return (hits, ndcgs) -+ -+ # open file to overwrite -+ r = open("./movielens_ratings.txt", 'w') -+ # Single thread -+ for idx in range(len(_testRatings)): -+ (hr,ndcg) = infer_one_rating(idx, r) -+ hits.append(hr) -+ ndcgs.append(ndcg) -+ return (hits, ndcgs) -+def infer_one_rating(idx, r): -+ rating = _testRatings[idx] -+ items = _testNegatives[idx] -+ u = rating[0] -+ gtItem = rating[1] -+ items.append(gtItem) -+ -+ # Get prediction scores -+ map_item_score = {} -+ users = np.full(len(items), u, dtype = 'int32') -+ predictions = _model.predict([users, np.array(items)], -+ batch_size=100, verbose=0) -+ for i in range(len(items)): -+ item = items[i] -+ map_item_score[item] = predictions[i] -+ -+ # Evaluate top rank list -+ ranklist = heapq.nlargest(_K, map_item_score, key=map_item_score.get) -+ -+ r.write("user : %s\n" % u) -+ r.write("items : %s\n" % items) -+ r.write("predicted_max_rating_item : %s\n" % ranklist[0]) -+ r.write("predicted_max_rating_prob : %s\n" % map_item_score[ranklist[0]]) -+ r.write("Top 10 Ratings:\n") -+ for i in range(len(ranklist)): -+ r.write("%s : %s\n" % (int(ranklist[i]), float(map_item_score[ranklist[i]]))) -+ r.write("#########################################################\n") -+ -+ hr = getHitRatio(ranklist, gtItem) -+ ndcg = getNDCG(ranklist, gtItem) -+ items.pop() -+ return (hr, ndcg) -+ - def evaluate_model(model, testRatings, testNegatives, K, num_thread): - """ - Evaluate the performance (Hit_Ratio, NDCG) of top-K recommendation -@@ -44,7 +109,7 @@ def evaluate_model(model, testRatings, testNegatives, K, num_thread): - ndcgs = [r[1] for r in res] - return (hits, ndcgs) - # Single thread -- for idx in xrange(len(_testRatings)): -+ for idx in range(len(_testRatings)): - (hr,ndcg) = eval_one_rating(idx) - hits.append(hr) - ndcgs.append(ndcg) -@@ -61,15 +126,15 @@ def eval_one_rating(idx): - users = np.full(len(items), u, dtype = 'int32') - predictions = _model.predict([users, np.array(items)], - batch_size=100, verbose=0) -- for i in xrange(len(items)): -+ for i in range(len(items)): - item = items[i] - map_item_score[item] = predictions[i] -- items.pop() - - # Evaluate top rank list - ranklist = heapq.nlargest(_K, map_item_score, key=map_item_score.get) - hr = getHitRatio(ranklist, gtItem) - ndcg = getNDCG(ranklist, gtItem) -+ items.pop() - return (hr, ndcg) - - def getHitRatio(ranklist, gtItem): -@@ -79,7 +144,7 @@ def getHitRatio(ranklist, gtItem): - return 0 - - def getNDCG(ranklist, gtItem): -- for i in xrange(len(ranklist)): -+ for i in range(len(ranklist)): - item = ranklist[i] - if item == gtItem: - return math.log(2) / math.log(i+2) diff --git a/samples/opensource/sampleMovieLensMPS/CMakeLists.txt b/samples/opensource/sampleMovieLensMPS/CMakeLists.txt deleted file mode 100644 index fde4c2fe..00000000 --- a/samples/opensource/sampleMovieLensMPS/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# 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. -# -SET(SAMPLE_SOURCES - sampleMovieLensMPS.cpp -) - -set(SAMPLE_PARSERS "uff") - -include(../../CMakeSamplesTemplate.txt) diff --git a/samples/opensource/sampleMovieLensMPS/README.md b/samples/opensource/sampleMovieLensMPS/README.md deleted file mode 100644 index 80f998d8..00000000 --- a/samples/opensource/sampleMovieLensMPS/README.md +++ /dev/null @@ -1,173 +0,0 @@ -# Movie Recommendation Using MPS (Multi-Process Service) - - -**Table Of Contents** -- [Description](#description) -- [How does this sample work?](#how-does-this-sample-work) - * [Importing a network to TensorRT](#importing-a-network-to-tensorrt) - * [Running inference](#running-inference) - * [Verifying the output](#verifying-the-output) - * [TensorRT API layers and ops](#tensorrt-api-layers-and-ops) -- [Training an NCF network](#training-an-ncf-network) -- [Preparing sample data](#preparing-sample-data) -- [Running the sample](#running-the-sample) - * [Sample `--help` options](#sample-help-options) -- [Additional resources](#additional-resources) -- [License](#license) -- [Changelog](#changelog) -- [Known issues](#known-issues) - -## Description - -This sample, sampleMovieLensMPS, is an end-to-end sample that imports a trained TensorFlow model and predicts the highest rated movie for each user using MPS (Multi-Process Service). - -MPS allows multiple CUDA processes to share single GPU context. With MPS, multiple overlapping kernel execution and `memcpy` operations from different processes can be scheduled concurrently to achieve maximum utilization. This can be especially effective in increasing parallelism for small networks with low resource utilization such as those primarily consisting of a series of small MLPs. - -This sample is identical to [sampleMovieLens](https://docs.nvidia.com/deeplearning/sdk/tensorrt-sample-support-guide/index.html#sample_movie) in terms of functionality, but is modified to support concurrent execution in multiple processes. Specifically, this sample demonstrates how to generate weights for a MovieLens dataset that TensorRT can then accelerate. - -**Note:** Currently, sampleMovieLensMPS supports only Linux x86-64 (includes Ubuntu and RedHat) desktop users. - -## How does this sample work? - -The network is trained in TensorFlow on the [MovieLens dataset](https://grouplens.org/datasets/movielens/) containing 6,040 users and 3,706 movies. The NCF recommender system is based off of the [Neural Collaborative Filtering](https://arxiv.org/abs/1708.05031) paper. - -Each query to the network consists of a `userID` and list of `MovieIDs`. The network predicts the highest-rated movie for each user. As trained parameters, the network has embeddings for users and movies, and weights for a sequence of MLPs. - -Specifically, this sample: -- [Imports a network into TensorRT](#importing-a-network-to-tensorrt) -- [Runs the inference](#running-inference) -- [Verifies its output](#verifying-the-output) - -### Importing a network to TensorRT - -The network is converted from Tensorflow using the UFF converter (see [Converting A Frozen Graph To UFF](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#samplecode3)), and imported using the UFF parser. Constant layers are used to represent the trained parameters within the network, and the MLPs are implemented using MatrixMultiply layers. A TopK operation is added manually after parsing to find the highest rated movie for the given user. - -### Running inference - -The sample fills the input buffer with `userIDs` and their corresponding lists of `MovieIDs`, which are loaded from `movielens_ratings.txt`. Then, it launches the inference to predict the rating probabilities for the movies using TensorRT. The inference will be launched on multiple processes. When MPS is enabled, the processes will share one single CUDA context to reduce context overhead. See [Multi-Process Service Introduction](https://docs.nvidia.com/deploy/mps/index.html) for more details about MPS. - -### Verifying the output - -Finally, the sample compares the outputs predicted by TensorRT with the expected outputs which are given by `movielens_ratings.txt`. For each user, the `MovieID` with the highest probability should match the expected highest-rated `MovieID`. In the verbose mode, the sample also prints out the probability, which should be close to the expected probability. - -### TensorRT API layers and ops - -In this sample, the following layers are used. For more information about these layers, see the [TensorRT Developer Guide: Layers](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#layers) documentation. - -[Activation layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#activation-layer) -The Activation layer implements element-wise activation functions. - -[MatrixMultiply layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#matrixmultiply-layer) -The MatrixMultiply layer implements matrix multiplication for a collection of matrices. - -[Scale layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#scale-layer) -The Scale layer implements a per-tensor, per-channel, or per-element affine transformation and/or exponentiation by constant values. - -[Shuffle layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#shuffle-layer) -The Shuffle layer implements a reshape and transpose operator for tensors. - -[TopK layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#topk-layer) -The TopK layer finds the top `K` maximum (or minimum) elements along a dimension, returning a reduced tensor and a tensor of index positions. - -## Training an NCF network - -This sample comes with a pre-trained model. However, if you want to train your own model, you would need to also convert the model weights to UFF format before you can run the sample. For step-by-step instructions, refer to the `README.md` file in the [sampleMovieLens](../sampleMovieLens) directory. - -## Running the sample - -1. Compile the sample by following build instructions in [TensorRT README](https://github.com/NVIDIA/TensorRT/). - -2. Set-up an MPS server. - ```bash - export CUDA_VISIBLE_DEVICES= - nvidia-smi -i -c EXCLUSIVE_PROCESS - export CUDA_VISIBLE_DEVICES=0 - export CUDA_MPS_PIPE_DIRECTORY=/tmp/nvidia-mps # Select a location that's accessible to the given $UID - export CUDA_MPS_LOG_DIRECTORY=/tmp/nvidia-log # Select a location that's accessible to the given $UID - nvidia-cuda-mps-control -d # Start the daemon. - ``` - The log files of MPS are located at: - ```bash - $CUDA_MPS_LOG_DIRECTORY/control.log - $CUDA_MPS_LOG_DIRECTORY/server.log - ``` - -3. Set-up an MPS client. Set the following variables in the client process environment. The `CUDA_VISIBLE_DEVICES` variable should not be set in the client's environment. - ```bash - export CUDA_MPS_PIPE_DIRECTORY=/tmp/nvidia-mps # Set to the same location as the MPS control daemon - export CUDA_MPS_LOG_DIRECTORY=/tmp/nvidia-log # Set to the same location as the MPS control daemon - ``` - -4. Run the sample from an MPS client to predict the highest-rated movie for each user on multiple processes. - ```bash - sample_movielens_mps (default batch=32 i.e. num of users, Number of processes=1) - sample_movielens_mps -b -p (bSize=Batch size i.e. num of users, nbProc=Number of processes) - sample_movielens_mps --verbose (prints inputs, groundtruth values, expected vs predicted probabilities) - ``` - -5. Verify that the sample ran successfully. If the sample runs successfully you should see output similar to the following: - ``` - &&&& RUNNING TensorRT.sample_movielens_mps # build/cuda- 10.0/7.3/x86_64/sample_movielens_mps -b 2 -p 2 - [I] data/samples/movielens/movielens_ratings.txt - [I] Begin parsing model... - [I] End parsing model... - [I] End building engine... - [I] Done execution in process: 24136 . Duration : 214.272 microseconds. - [I] Num of users : 2 - [I] Num of Movies : 100 - [I] | PID : 24136 | User : 0 | Expected Item : 128 | Predicted Item : 128 | - [I] | PID : 24136 | User : 1 | Expected Item : 133 | Predicted Item : 133 | - [I] Done execution in process: 24135 . Duration : 214.176 microseconds. - [I] Num of users : 2 - [I] Num of Movies : 100 - [I] | PID : 24135 | User : 0 | Expected Item : 128 | Predicted Item : 128 | - [I] | PID : 24135 | User : 1 | Expected Item : 133 | Predicted Item : 133 | - [I] Number of processes executed: 2. Number of processes failed: 0. - [I] Total MPS Run Duration: 1737.51 milliseconds. - &&&& PASSED TensorRT.sample_movielens_mps # build/cuda- 10.0/7.3/x86_64/sample_movielens_mps -b 2 -p 2 - ``` - This output shows that the sample ran successfully; `PASSED`. The output also shows that the predicted items for each user matches the expected items and the duration of the execution. Finally, the sample prints out the PIDs of the processes, showing that the inference is launched on multiple processes. - -6. To restore the system to its original state, shutdown MPS, if needed. - ```bash - echo quit | nvidia-cuda-mps-control - ``` - - -### Sample `--help` options - -To see the full list of available options and their descriptions, use the `-h` or `--help` command line option. - - -# Additional resources - -The following resources provide a deeper understanding about sampleMovieLensMPS: - -**MovieLensMPS** -- [MovieLens dataset](https://grouplens.org/datasets/movielens/) -- [Neural Collaborative Filtering Paper](https://arxiv.org/abs/1708.05031) -- [Multi-Process Service Introduction](https://docs.nvidia.com/deploy/mps/index.html) - -**Models** -- [Neural Collaborative Filtering GitHub Repo](https://github.com/hexiangnan/neural_collaborative_filtering) - -**Documentation** -- [Introduction To NVIDIA’s TensorRT Samples](https://docs.nvidia.com/deeplearning/sdk/tensorrt-sample-support-guide/index.html#samples) -- [Jupyter Notebook Tutorial for SampleMovieLens](https://developer.download.nvidia.com/compute/machine-learning/tensorrt/models/sampleMLP-notebook.html?ncid=--47568) -- [Working With TensorRT Using The C++ API](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#c_topics) -- [NVIDIA’s TensorRT Documentation Library](https://docs.nvidia.com/deeplearning/sdk/tensorrt-archived/index.html) - -# 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 - -February 2019 -This `README.md` file was recreated, updated and reviewed. - - -# Known issues - -- Since the UFF converter is not currently supported on Windows, the model cannot be converted to UFF on Windows systems. It is still possible to use the UFF file shipped with the sample. diff --git a/samples/opensource/sampleMovieLensMPS/sampleMovieLensMPS.cpp b/samples/opensource/sampleMovieLensMPS/sampleMovieLensMPS.cpp deleted file mode 100644 index c2eef90d..00000000 --- a/samples/opensource/sampleMovieLensMPS/sampleMovieLensMPS.cpp +++ /dev/null @@ -1,790 +0,0 @@ -/* - * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Required to enable MPS Support -#include -#include -#include - -#ifndef _MSC_VER -#include -#include -#include -#include -#include -#include -#include -#include -#endif - -#include "NvInfer.h" -#include "NvUffParser.h" -#include "common.h" -#include "logger.h" - -using namespace nvinfer1; -using namespace nvuffparser; - -const std::string gSampleName = "TensorRT.sample_movielens_mps"; - -// constants that are known about the MovieLens (NCF) MLP network. -static const int32_t NUM_USERS{32}; // Total number of users. -static const int32_t TOPK_MOVIES{1}; // The output of the topK layer for MovieLens sample. -static const int32_t NUM_INDICES{100}; // Total numbers of Movies to predict per user. -static const int32_t EMBEDDING_VEC_SIZE{32}; // Embedding vector size of each user and item. -static const int32_t THREADS{1}; -static const char* USER_BLOB_NAME{"user_input"}; // user input blob name. -static const char* ITEM_BLOB_NAME{"item_input"}; // item input blob name. -static const char* TOPK_ITEM_PROB{"topk_values"}; // predicted item probability blob name. -static const char* TOPK_ITEM_NAME{"topk_items"}; // predicted item probability blob name. -static const char* RATING_INPUT_FILE{ - "movielens_ratings.txt"}; // The default input file with 50 users and groundtruth data. -static const char* DEFAULT_WEIGHT_FILE{"sampleMovieLens.wts2"}; // The weight file produced from README.txt -static const char* UFF_MODEL_FILE{"sampleMovieLens.uff"}; -static const char* UFF_OUTPUT_NODE{"prediction/Sigmoid"}; -static const char* ENGINE_FILE{"sampleMovieLens.engine"}; -static const int32_t DEVICE{0}; -static const std::vector directories{"data/samples/movielens/", "data/movielens/"}; - -template -using SampleUniquePtr = std::unique_ptr; - -class Semaphore -{ -public: - Semaphore(const char* semName) - : mSemName(semName) - { - } - - ~Semaphore() - { - sem_unlink(mSemName); - sem_close(mSemEngine); - } - - void wait() - { - sem_wait(mSemEngine); - } - - void post() - { - sem_post(mSemEngine); - } - - void open() - { - mSemEngine = sem_open(mSemName, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0); - if (mSemEngine == SEM_FAILED) - { - throw std::runtime_error("Could not create semaphore"); - } - } - -private: - const char* mSemName; - sem_t* mSemEngine; -}; - -class SharedMemory -{ -public: - SharedMemory(const char* modelStreamFd) - : mModelStreamFd(modelStreamFd) - { - } - - ~SharedMemory() - { - shm_unlink(mModelStreamFd); - } - - int open_ro() - { - return open(O_RDONLY, 0666); - } - - int open_rw() - { - return open(O_RDWR | O_CREAT, 0666); - } - -private: - int open(int flag, mode_t mode) - { - int fd = shm_open(mModelStreamFd, flag, mode); - if (fd <= 0) - { - throw std::runtime_error("Could not create file descriptor: /dev/shm" + std::string(mModelStreamFd)); - } - return fd; - } - const char* mModelStreamFd; -}; - -// The OutptutArgs struct holds intermediate/final outputs generated by the MovieLens structure per user. -struct OutputArgs -{ - int32_t userId; // The user Id per batch. - int32_t expectedPredictedMaxRatingItem; // The Expected Max Rating Item per user (inference ground truth). - float expectedPredictedMaxRatingItemProb; // The Expected Max Rating Probability. (inference ground truth). - std::vector allItems; // All inferred items per user. - std::vector> itemProbPairVec; // Expected topK items and prob per user. -}; // struct pargs - -struct Args -{ - int32_t embeddingVecSize{EMBEDDING_VEC_SIZE}; - int32_t numUsers{NUM_USERS}; // Total number of users. Should be equal to ratings file users count. - int32_t topKMovies{TOPK_MOVIES}; // TopK movies per user. - int32_t numMoviesPerUser{NUM_INDICES}; // The number of movies per user. - int32_t nbProcesses{THREADS}; // Number of concurrent processes - std::string weightFile{DEFAULT_WEIGHT_FILE}; // Weight file (.wts2) format Movielens sample. - std::string ratingInputFile{RATING_INPUT_FILE}; // The input rating file. - std::string uffFile{UFF_MODEL_FILE}; - std::string engineFile{ENGINE_FILE}; - bool enableFP16{false}; // Enable ability to run in FP16 mode. - bool enableInt8{false}; // Enable ability to run in Int8 mode. - bool enableVerbose{false}; // Set reportable severity of logger to kVERBOSE. - bool help{false}; // Print help info. - int useDLACore{-1}; - // The below structures are used to compare the predicted values to inference (ground truth) - std::map> userToItemsMap; // Lookup for inferred items for each user. - std::map>> - userToExpectedItemProbMap; // Lookup for topK items and probs for each user. - int32_t device{DEVICE}; - std::vector pargsVec; - std::atomic failCount; // Number threads that failed inference. -}; // struct args - -struct Batch -{ - Batch(ICudaEngine* engine, void* userInputPtr, void* itemInputPtr, const Args& args) - { - mEngine = engine; - mContext = SampleUniquePtr(mEngine->createExecutionContext()); - - CHECK(cudaStreamCreate(&mStream)); - - // In order to bind the buffers, we need to know the names of the input and output tensors. - // note that indices are guaranteed to be less than IEngine::getNbBindings() - int userInputIndex = mEngine->getBindingIndex(USER_BLOB_NAME); - int itemInputIndex = mEngine->getBindingIndex(ITEM_BLOB_NAME); - int outputPredictionIndex = mEngine->getBindingIndex(UFF_OUTPUT_NODE); - int outputItemProbIndex = mEngine->getBindingIndex(TOPK_ITEM_PROB); - int outputItemNameIndex = mEngine->getBindingIndex(TOPK_ITEM_NAME); - - mMemSizes.push_back(args.numUsers * args.numMoviesPerUser * sizeof(float)); - mMemSizes.push_back(args.numUsers * args.numMoviesPerUser * sizeof(float)); - mMemSizes.push_back(args.numUsers * args.numMoviesPerUser * sizeof(float)); - mMemSizes.push_back(args.numUsers * args.topKMovies * sizeof(float)); - mMemSizes.push_back(args.numUsers * args.topKMovies * sizeof(float)); - - CHECK(cudaMallocHost(&mHostMemory[userInputIndex], mMemSizes[userInputIndex])); - CHECK(cudaMallocHost(&mHostMemory[itemInputIndex], mMemSizes[itemInputIndex])); - CHECK(cudaMallocHost(&mHostMemory[outputPredictionIndex], mMemSizes[outputPredictionIndex])); - CHECK(cudaMallocHost(&mHostMemory[outputItemProbIndex], mMemSizes[outputItemProbIndex])); - CHECK(cudaMallocHost(&mHostMemory[outputItemNameIndex], mMemSizes[outputItemNameIndex])); - - // copy the data to host memory - for (unsigned int i = 0; i < (mMemSizes[userInputIndex]) / sizeof(float); ++i) - { - *(static_cast(mHostMemory[userInputIndex]) + i) = *((uint32_t*) userInputPtr + i); - } - for (unsigned int i = 0; i < (mMemSizes[itemInputIndex]) / sizeof(float); ++i) - { - *(static_cast(mHostMemory[itemInputIndex]) + i) = *((uint32_t*) itemInputPtr + i); - } - - // allocate GPU memory - CHECK(cudaMalloc(&mDeviceMemory[userInputIndex], mMemSizes[userInputIndex])); - CHECK(cudaMalloc(&mDeviceMemory[itemInputIndex], mMemSizes[itemInputIndex])); - CHECK(cudaMalloc(&mDeviceMemory[outputPredictionIndex], mMemSizes[outputPredictionIndex])); - CHECK(cudaMalloc(&mDeviceMemory[outputItemProbIndex], mMemSizes[outputItemProbIndex])); - CHECK(cudaMalloc(&mDeviceMemory[outputItemNameIndex], mMemSizes[outputItemNameIndex])); - } - - ~Batch() - { - for (auto p : mHostMemory) - CHECK(cudaFreeHost(p)); - for (auto p : mDeviceMemory) - CHECK(cudaFree(p)); - CHECK(cudaStreamDestroy(mStream)); - } - - ICudaEngine* mEngine; - SampleUniquePtr mContext; - cudaStream_t mStream; - void* mHostMemory[5]; - void* mDeviceMemory[5]; - std::vector mMemSizes; -}; - -void printHelpInfo() -{ - std::cout - << "Usage:\n" - << " ./sample_movielens_mps [-h or --help] [-b NUM_USERS] [-p NUM_PROCESSES] [--useDLACore=] [--verbose]\n" - << "-h Display help information. All single dash options enable perf mode.\n" - << "-b Number of Users i.e. Batch Size (default numUsers=32).\n" - << "-p Number of child processes to launch (default nbProcesses=1. Using MPS with this option is " - "strongly recommended).\n" - << "--useDLACore=N Specify a DLA engine for layers that support DLA. Value can range from 0 to n-1, where n is " - "the number of DLA engines on the platform.\n" - << "--verbose Enable verbose prints.\n" - << "--int8 Run in Int8 mode.\n" - << "--fp16 Run in FP16 mode.\n" - << std::endl; -} - -// Parse the arguments and return failure if arguments are incorrect -bool parseArgs(Args& args, int argc, char* argv[]) -{ - for (int i = 1; i < argc; ++i) - { - std::string argStr(argv[i]); - - if (argStr == "-h" || argStr == "--help") - { - args.help = true; - return true; - } - if (argStr == "-b") - { - i++; - args.numUsers = std::atoi(argv[i]); - } - else if (argStr == "-p") - { - i++; - args.nbProcesses = std::atoi(argv[i]); - } - else if (argStr == "--verbose") - { - args.enableVerbose = true; - sample::setReportableSeverity(ILogger::Severity::kVERBOSE); - } - else if (argStr.compare(0, 13, "--useDLACore=") == 0 && argStr.size() > 13) - { - args.useDLACore = std::stoi(argv[i] + 13); - } - else if (argStr == "--int8") - { - args.enableInt8 = true; - } - else if (argStr == "--fp16") - { - args.enableFP16 = true; - } - else - { - return false; - } - } - return true; -} - -void printOutputArgs(OutputArgs& pargs) -{ - sample::gLogVerbose << "User Id : " << pargs.userId << std::endl; - sample::gLogVerbose << "Expected Predicted Max Rating Item : " << pargs.expectedPredictedMaxRatingItem - << std::endl; - sample::gLogVerbose << "Expected Predicted Max Rating Prob : " << pargs.expectedPredictedMaxRatingItemProb - << std::endl; - sample::gLogVerbose << "Total TopK Items : " << pargs.itemProbPairVec.size() << std::endl; - for (unsigned i = 0; i < pargs.itemProbPairVec.size(); ++i) - sample::gLogVerbose << pargs.itemProbPairVec.at(i).first << " : " << pargs.itemProbPairVec.at(i).second - << std::endl; -} - -std::string readNextLine(std::ifstream& file, char delim) -{ - std::string line; - std::getline(file, line); - auto pos = line.find(delim); - line = line.substr(pos + 1); - return line; -} - -void readInputSample(std::ifstream& file, OutputArgs& pargs, std::string line, const Args& args) -{ - // read user name - char delim = ':'; - auto pos = line.find(delim); - line = line.substr(pos + 1); - pargs.userId = std::stoi(line); - // read items - std::string items = readNextLine(file, delim); - items = items.substr(2, items.size() - 2); - std::stringstream ss(items); - std::string i; - while (ss >> i) - { - if (ss.peek() == ',' || ss.peek() == ' ') - ss.ignore(); - i = i.substr(0, i.size() - 1); - pargs.allItems.push_back(std::stoi(i)); - } - - // read expected predicted max rating item - pargs.expectedPredictedMaxRatingItem = std::stoi(readNextLine(file, delim)); - - // read expected predicted max rating prob - std::string prob = readNextLine(file, delim); - prob = prob.substr(2, prob.size() - 3); - pargs.expectedPredictedMaxRatingItemProb = std::stof(prob); - - // skip line - std::getline(file, line); - std::getline(file, line); - - // read all the top 10 prediction ratings - for (int i = 0; i < 10; ++i) - { - auto pos = line.find(delim); - int32_t item = std::stoi(line.substr(0, pos - 1)); - float prob = std::stof(line.substr(pos + 2)); - pargs.itemProbPairVec.emplace_back((std::make_pair(item, prob))); - std::getline(file, line); - } -} - -void parseMovieLensData(Args& args) -{ - std::ifstream file; - file.open(args.ratingInputFile); - std::string line; - int userIdx = 0; - while (std::getline(file, line) && userIdx < args.numUsers) - { - OutputArgs pargs; - readInputSample(file, pargs, line, args); - - // store the pargs in the global data structure. Hack. - args.pargsVec.push_back(pargs); - - args.userToItemsMap[userIdx] = std::move(pargs.allItems); - args.userToExpectedItemProbMap[userIdx] = std::move(pargs.itemProbPairVec); - - userIdx++; - printOutputArgs(pargs); - } - - // number of users should be equal to number of users in rating file - if (args.numUsers != userIdx) - { - throw std::runtime_error("Invalid ratings file."); - } -} - -template -bool printInferenceOutput( - void* userInputPtr, void* itemInputPtr, void* topKItemNumberPtr, void* topKItemProbPtr, const Args& args) -{ - bool pass{true}; - T1* userInput{static_cast(userInputPtr)}; - T1* topKItemNumber{static_cast(topKItemNumberPtr)}; - T2* topKItemProb{static_cast(topKItemProbPtr)}; - - sample::gLogInfo << "Num of users : " << args.numUsers << std::endl; - sample::gLogInfo << "Num of Movies : " << args.numMoviesPerUser << std::endl; - - sample::gLogVerbose << "|-----------|------------|-----------------|-----------------|" << std::endl; - sample::gLogVerbose << "| User | Item | Expected Prob | Predicted Prob |" << std::endl; - sample::gLogVerbose << "|-----------|------------|-----------------|-----------------|" << std::endl; - - for (int i = 0; i < args.numUsers; ++i) - { - int userIdx = userInput[i * args.numMoviesPerUser]; - int maxPredictedIdx = topKItemNumber[i * args.topKMovies]; - int maxExpectedItem = args.userToExpectedItemProbMap.at(userIdx).at(0).first; - int maxPredictedItem = args.userToItemsMap.at(userIdx).at(maxPredictedIdx); - pass &= (maxExpectedItem == maxPredictedItem); - - for (int k = 0; k < args.topKMovies; ++k) - { - int predictedIdx = topKItemNumber[i * args.topKMovies + k]; - float predictedProb = topKItemProb[i * args.topKMovies + k]; - float expectedProb = args.userToExpectedItemProbMap.at(userIdx).at(k).second; - int predictedItem = args.userToItemsMap.at(userIdx).at(predictedIdx); - sample::gLogVerbose << "|" << std::setw(10) << userIdx << " | " << std::setw(10) << predictedItem << " | " - << std::setw(15) << expectedProb << " | " << std::setw(15) << predictedProb << " | " - << std::endl; - } - } - - for (int i = 0; i < args.numUsers; ++i) - { - int userIdx = userInput[i * args.numMoviesPerUser]; - int maxPredictedIdx = topKItemNumber[i * args.topKMovies]; - int maxExpectedItem = args.userToExpectedItemProbMap.at(userIdx).at(0).first; - int maxPredictedItem = args.userToItemsMap.at(userIdx).at(maxPredictedIdx); - sample::gLogInfo << "| PID : " << std::setw(4) << getpid() << " | User :" << std::setw(4) << userIdx - << " | Expected Item :" << std::setw(5) << maxExpectedItem - << " | Predicted Item :" << std::setw(5) << maxPredictedItem << " | " << std::endl; - } - - return pass; -} - -bool submitWork(Batch& b, const Args& args) -{ - int userInputIndex = b.mEngine->getBindingIndex(USER_BLOB_NAME); - int itemInputIndex = b.mEngine->getBindingIndex(ITEM_BLOB_NAME); - int outputPredictionIndex = b.mEngine->getBindingIndex(UFF_OUTPUT_NODE); - int outputItemProbIndex = b.mEngine->getBindingIndex(TOPK_ITEM_PROB); - int outputItemNameIndex = b.mEngine->getBindingIndex(TOPK_ITEM_NAME); - - // Copy input from host to device - CHECK(cudaMemcpyAsync(b.mDeviceMemory[userInputIndex], b.mHostMemory[userInputIndex], b.mMemSizes[userInputIndex], - cudaMemcpyHostToDevice, b.mStream)); - CHECK(cudaMemcpyAsync(b.mDeviceMemory[itemInputIndex], b.mHostMemory[itemInputIndex], b.mMemSizes[itemInputIndex], - cudaMemcpyHostToDevice, b.mStream)); - - if (!b.mContext->enqueue(args.numUsers, b.mDeviceMemory, b.mStream, nullptr)) - { - return false; - } - - // copy output from device to host - CHECK(cudaMemcpyAsync(b.mHostMemory[outputPredictionIndex], b.mDeviceMemory[outputPredictionIndex], - b.mMemSizes[outputPredictionIndex], cudaMemcpyDeviceToHost, b.mStream)); - CHECK(cudaMemcpyAsync(b.mHostMemory[outputItemProbIndex], b.mDeviceMemory[outputItemProbIndex], - b.mMemSizes[outputItemProbIndex], cudaMemcpyDeviceToHost, b.mStream)); - CHECK(cudaMemcpyAsync(b.mHostMemory[outputItemNameIndex], b.mDeviceMemory[outputItemNameIndex], - b.mMemSizes[outputItemNameIndex], cudaMemcpyDeviceToHost, b.mStream)); - - return true; -} - -std::shared_ptr loadModelAndCreateEngine(const char* uffFile, IUffParser* parser, const Args& args) -{ - // Create the builder - auto builder = SampleUniquePtr(nvinfer1::createInferBuilder(sample::gLogger.getTRTLogger())); - if (builder == nullptr) - { - throw std::runtime_error("Could not create builder."); - } - - auto network = SampleUniquePtr(builder->createNetwork()); - if (network == nullptr) - { - throw std::runtime_error("Could not create network."); - } - - auto config = SampleUniquePtr(builder->createBuilderConfig()); - if (config == nullptr) - { - throw std::runtime_error("Could not create network."); - } - - sample::gLogInfo << "Begin parsing model..." << std::endl; - - auto dType = args.enableFP16 ? nvinfer1::DataType::kHALF : nvinfer1::DataType::kFLOAT; - - // Parse the uff model to populate the network - if (!parser->parse(uffFile, *network, dType)) - { - sample::gLogError << "Failure while parsing UFF file" << std::endl; - return nullptr; - } - - sample::gLogInfo << "End parsing model..." << std::endl; - - // Add postprocessing i.e. topk layer to the UFF Network - // Retrieve last layer of UFF Network - auto uffLastLayer = network->getLayer(network->getNbLayers() - 1); - - // Reshape output of fully connected layer numOfMovies x 1 x 1 x 1 to numOfMovies x 1 x 1. - auto reshapeLayer = network->addShuffle(*uffLastLayer->getOutput(0)); - reshapeLayer->setReshapeDimensions(Dims3{1, args.numMoviesPerUser, 1}); - if (reshapeLayer == nullptr) - { - throw std::runtime_error("Could not create reshape layer."); - } - - // Apply TopK layer to retrieve item probabilities and corresponding index number. - auto topK = network->addTopK(*reshapeLayer->getOutput(0), TopKOperation::kMAX, args.topKMovies, 0x2); - if (topK == nullptr) - { - throw std::runtime_error("Could not create TopK layer."); - } - - // Mark outputs for index and probs. Also need to set the item layer type == kINT32. - topK->getOutput(0)->setName(TOPK_ITEM_PROB); - topK->getOutput(1)->setName(TOPK_ITEM_NAME); - - // Specify topK tensors as outputs - network->markOutput(*topK->getOutput(0)); - network->markOutput(*topK->getOutput(1)); - - // Set the topK indices tensor as INT32 type - topK->getOutput(1)->setType(DataType::kINT32); - - // Build the engine - builder->setMaxBatchSize(args.numUsers); - config->setMaxWorkspaceSize(1_GiB); // The _GiB literal operator is defined in common.h - if (args.enableFP16) - { - config->setFlag(BuilderFlag::kFP16); - } - if (args.enableInt8) - { - config->setFlag(BuilderFlag::kINT8); - } - - samplesCommon::setDummyInt8Scales(config.get(), network.get()); - samplesCommon::enableDLA(builder.get(), config.get(), args.useDLACore); - - auto engine = samplesCommon::infer_object(builder->buildEngineWithConfig(*network, *config)); - if (!engine) - { - sample::gLogError << "Unable to create engine" << std::endl; - return nullptr; - } - - sample::gLogInfo << "End building engine..." << std::endl; - return engine; -} - -bool doInference(void* modelStreamData, int modelStreamSize, void* userInputPtr, void* itemInputPtr, Args& args) -{ - auto runtime = SampleUniquePtr(nvinfer1::createInferRuntime(sample::gLogger.getTRTLogger())); - if (args.useDLACore >= 0) - { - runtime->setDLACore(args.useDLACore); - } - - auto engine - = samplesCommon::infer_object(runtime->deserializeCudaEngine(modelStreamData, modelStreamSize, nullptr)); - - Batch b{engine.get(), userInputPtr, itemInputPtr, args}; - - { - samplesCommon::GpuTimer timer{b.mStream}; - timer.start(); - // Run inference for all the nbProcesses - if (!submitWork(b, args)) - { - sample::gLogError << "Error in submit work." << std::endl; - return false; - } - cudaStreamSynchronize(b.mStream); - timer.stop(); - sample::gLogInfo << "Done execution in process: " << getpid() << " . Duration : " << timer.microseconds() - << " microseconds." << std::endl; - } - - int outputItemProbIndex = b.mEngine->getBindingIndex(TOPK_ITEM_PROB); - int outputItemNameIndex = b.mEngine->getBindingIndex(TOPK_ITEM_NAME); - - float* topKItemProb = static_cast(b.mHostMemory[outputItemProbIndex]); - uint32_t* topKItemNumber = static_cast(b.mHostMemory[outputItemNameIndex]); - bool pass{printInferenceOutput(userInputPtr, itemInputPtr, topKItemNumber, topKItemProb, args)}; - - return pass; -} - -int mainMovieLensMPS(Args& args, OutputArgs& pargs) -{ - // Parse the ratings file and populate ground truth data - args.ratingInputFile = locateFile(args.ratingInputFile, directories); - sample::gLogInfo << args.ratingInputFile << std::endl; - - // Parse ground truth data and inputs, common to all processes (if using MPS) - parseMovieLensData(args); - - // Create uff parser - args.uffFile = locateFile(args.uffFile, directories); - auto parser = SampleUniquePtr(nvuffparser::createUffParser()); - - // All nbProcesses should wait until the parent is done building the engine. - Semaphore sem("/engine_built"); - sem.open(); - - pid_t pid{}; - // Create child processes - for (int i = 0; i < args.nbProcesses; ++i) - { - pid = fork(); - // Children should not create additional processes. - if (pid == 0) - { - break; - } - else if (pid == -1) - { - throw std::runtime_error("Could not create child process"); - } - } - // Every process needs to know if it's a child or not. - bool isParentProcess = (pid != 0); - - SharedMemory shm("/sampleMovieLens.modelStream"); - - if (isParentProcess) - { - // Parent process should build an engine and write it to the shared buffer. - Dims inputIndices; - inputIndices.nbDims = 3; - inputIndices.d[0] = args.numMoviesPerUser; - inputIndices.d[1] = 1; - inputIndices.d[2] = 1; - - parser->registerInput(USER_BLOB_NAME, inputIndices, UffInputOrder::kNCHW); - parser->registerInput(ITEM_BLOB_NAME, inputIndices, UffInputOrder::kNCHW); - parser->registerOutput(UFF_OUTPUT_NODE); - - auto engine = loadModelAndCreateEngine(args.uffFile.c_str(), parser.get(), args); - if (engine.get() == nullptr) - { - throw std::runtime_error("Failed to create engine."); - } - - auto modelStream = samplesCommon::infer_object(engine->serialize()); - - size_t modelStreamSize = modelStream->size(); - // Create a shared buffer for the modelStream. - int fd = shm.open_rw(); - - fallocate(fd, 0, 0, modelStreamSize); - void* modelStreamData = mmap(NULL, modelStreamSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - // Copy modelStream to the shared buffer. - std::memcpy(modelStreamData, modelStream->data(), modelStreamSize); - // Clean up. - close(fd); - } - else - { - // Allocate input and output buffers on host. - std::vector userInput(args.numUsers * args.numMoviesPerUser * sizeof(float)); - std::vector itemInput(args.numUsers * args.numMoviesPerUser * sizeof(float)); - - for (int i = 0; i < args.numUsers; ++i) - { - for (int k = 0; k < args.numMoviesPerUser; ++k) - { - int idx = i * args.numMoviesPerUser + k; - userInput[idx] = args.pargsVec[i].userId; - itemInput[idx] = args.pargsVec[i].allItems.at(k); - } - } - - // Now wait for parent to construct engine and write the modelstream to the shared buffer. - sem.wait(); - - // Open a file descriptor for the shared buffer. - int fd = shm.open_ro(); - - // Get size of shared memory buffer. - struct stat sb; - fstat(fd, &sb); - int modelStreamSize = sb.st_size; - if (modelStreamSize <= 0) - { - throw std::runtime_error("Failed to fetch model stream from shared memory buffer."); - } - - // Retrieve the modelStream and close the file descriptor. - void* modelStreamData = mmap(NULL, modelStreamSize, PROT_READ, MAP_SHARED, fd, 0); - close(fd); - - // All child processes will do inference and then exit. - bool pass = doInference(modelStreamData, modelStreamSize, userInput.data(), itemInput.data(), args); - if (!pass) - args.failCount++; - - exit(0); - } - - // Let children processes continue - for (int j = 0; j < args.nbProcesses; ++j) - { - sem.post(); - } - - // Then time them. - { - samplesCommon::PreciseCpuTimer timer{}; - timer.start(); - int status; - // Parent should wait for child processes. - for (int i = 0; i < args.nbProcesses; ++i) - { - wait(&status); - } - timer.stop(); - sample::gLogInfo << "Number of processes executed : " << args.nbProcesses - << ". Total MPS Run Duration : " << timer.milliseconds() << " milliseconds." << std::endl; - } - - bool pass = !args.failCount; - return pass; -} - -int main(int argc, char* argv[]) -{ - Args args; // Global struct to store arguments - OutputArgs pargs; // Ratings file struct - - // Parse arguments - bool argsOK = parseArgs(args, argc, argv); - args.failCount = 0; - - if (args.help) - { - printHelpInfo(); - return EXIT_SUCCESS; - } - - if (!argsOK) - { - printHelpInfo(); - sample::gLogError << "Invalid arguments" << std::endl; - return EXIT_FAILURE; - } - - auto sampleTest = sample::gLogger.defineTest(gSampleName, argc, argv); - sample::gLogger.reportTestStart(sampleTest); - bool pass = false; - - try - { - pass = mainMovieLensMPS(args, pargs); - } - catch (const std::exception& e) - { - sample::gLogError << e.what() << std::endl; - } - return sample::gLogger.reportTest(sampleTest, pass); -} diff --git a/samples/opensource/sampleMovieLensMPS/sampleMovieLensTraining.patch b/samples/opensource/sampleMovieLensMPS/sampleMovieLensTraining.patch deleted file mode 100644 index 66fb4e8e..00000000 --- a/samples/opensource/sampleMovieLensMPS/sampleMovieLensTraining.patch +++ /dev/null @@ -1,419 +0,0 @@ -Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. - -NOTICE TO LICENSEE: - -This source code and/or documentation ("Licensed Deliverables") are subject to -NVIDIA intellectual property rights under U.S. and international Copyright -laws. - -These Licensed Deliverables contained herein is PROPRIETARY and CONFIDENTIAL -to NVIDIA and is being provided under the terms and conditions of a form of -NVIDIA software license agreement by and between NVIDIA and Licensee ("License -Agreement") or electronically accepted by Licensee. Notwithstanding any terms -or conditions to the contrary in the License Agreement, reproduction or -disclosure of the Licensed Deliverables to any third party without the express -written consent of NVIDIA is prohibited. - -NOTWITHSTANDING ANY TERMS OR CONDITIONS TO THE CONTRARY IN THE LICENSE -AGREEMENT, NVIDIA MAKES NO REPRESENTATION ABOUT THE SUITABILITY OF THESE -LICENSED DELIVERABLES FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS -OR IMPLIED WARRANTY OF ANY KIND. NVIDIA DISCLAIMS ALL WARRANTIES WITH REGARD -TO THESE LICENSED DELIVERABLES, INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE. -NOTWITHSTANDING ANY TERMS OR CONDITIONS TO THE CONTRARY IN THE LICENSE -AGREEMENT, IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, -INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THESE LICENSED DELIVERABLES. - -U.S. Government End Users. These Licensed Deliverables are a "commercial -item" as that term is defined at 48 C.F.R. 2.101 (OCT 1995), consisting of -"commercial computer software" and "commercial computer software -documentation" as such terms are used in 48 C.F.R. 12.212 (SEPT 1995) and is -provided to the U.S. Government only as a commercial end item. Consistent -with 48 C.F.R.12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (JUNE 1995), -all U.S. Government End Users acquire the Licensed Deliverables with only -those rights set forth herein. - -Any use of the Licensed Deliverables in individual and commercial software -must include, in the user documentation and internal comments to the code, the -above Disclaimer and U.S. Government End Users Notice. - -diff --git a/MLP.py b/MLP.py -index 70566c7..93c0d53 100644 ---- a/MLP.py -+++ b/MLP.py -@@ -1,30 +1,27 @@ - ''' - Created on Aug 9, 2016 - Keras Implementation of Multi-Layer Perceptron (GMF) recommender model in: --He Xiangnan et al. Neural Collaborative Filtering. In WWW 2017. -+He Xiangnan et al. Neural Collaborative Filtering. In WWW 2017. - - @author: Xiangnan He (xiangnanhe@gmail.com) - ''' -- -+import shutil - import numpy as np -+import tensorflow as tf - --import theano --import theano.tensor as T --import keras --from keras import backend as K --from keras import initializations --from keras.regularizers import l2, activity_l2 --from keras.models import Sequential, Graph, Model --from keras.layers.core import Dense, Lambda, Activation --from keras.layers import Embedding, Input, Dense, merge, Reshape, Merge, Flatten, Dropout --from keras.constraints import maxnorm --from keras.optimizers import Adagrad, Adam, SGD, RMSprop -+from tensorflow import keras -+from tensorflow.python.keras import initializers -+from tensorflow.python.keras.regularizers import l2 -+from tensorflow.python.keras.models import Model -+from tensorflow.python.keras.layers import Dense, Embedding, Input, Flatten, \ -+ concatenate -+from tensorflow.python.keras.optimizers import Adam, Adagrad, SGD, RMSprop -+from tensorflow.python.framework import graph_util - from evaluate import evaluate_model -+from evaluate import infer_model - from Dataset import Dataset - from time import time --import sys - import argparse --import multiprocessing as mp - - #################### Arguments #################### - def parse_args(): -@@ -54,7 +51,50 @@ def parse_args(): - return parser.parse_args() - - def init_normal(shape, name=None): -- return initializations.normal(shape, scale=0.01, name=name) -+ return initializers.he_normal() -+ -+def freeze_checkpoint_graph(output_node_names, checkpoint_model_folder, output_graph_filename): -+ # retrieve the checkpoint fullpath -+ checkpoint = tf.train.get_checkpoint_state(checkpoint_model_folder) -+ input_checkpoint = checkpoint.model_checkpoint_path -+ -+ print(input_checkpoint) -+ # precise the file fullname of our freezed graph -+ absolute_model_folder = "/".join(input_checkpoint.split("/")[:-1]) -+ -+ # we clear devices, to allow tensorflow to control on the loading, where it wants operations to be calculated -+ clear_devices = True -+ -+ # the checkpoint directory has - .meta and .data i.e. weights file to be retrieved -+ saver = tf.train.import_meta_graph(input_checkpoint + '.meta', clear_devices=clear_devices) -+ -+ # retrieve protobuf graph definition -+ # returns the default graph of the current thread - will be the innermost graph -+ # on which Graph.as_default() context has been entered - global_default_graph if non has been explicitly created -+ graph = tf.get_default_graph() -+ -+ # retrieve graph def for a grpah -+ input_graph_def = graph.as_graph_def() -+ -+ # print the output nodes -+ output_node_list = [n.name for n in tf.get_default_graph().as_graph_def().node] -+ -+ # start the session and restore the weights -+ with tf.Session() as sess: -+ saver.restore(sess, input_checkpoint) -+ -+ # in order to freeze the graph - need to export the variables to constants -+ output_graph_def = graph_util.convert_variables_to_constants( -+ sess, # session have weights stored -+ input_graph_def, -+ output_node_names.split(",") -+ ) -+ -+ # finally we serialize and dump the output graph to the filesystem -+ with tf.gfile.GFile(output_graph_filename, "wb") as f: -+ f.write(output_graph_def.SerializeToString()) -+ -+ print("[FREEZE_INFO] ", len(output_graph_def.node), " ops in the final graph.") - - def get_model(num_users, num_items, layers = [20,10], reg_layers=[0,0]): - assert len(layers) == len(reg_layers) -@@ -63,29 +103,43 @@ def get_model(num_users, num_items, layers = [20,10], reg_layers=[0,0]): - user_input = Input(shape=(1,), dtype='int32', name = 'user_input') - item_input = Input(shape=(1,), dtype='int32', name = 'item_input') - -- MLP_Embedding_User = Embedding(input_dim = num_users, output_dim = layers[0]/2, name = 'user_embedding', -- init = init_normal, W_regularizer = l2(reg_layers[0]), input_length=1) -- MLP_Embedding_Item = Embedding(input_dim = num_items, output_dim = layers[0]/2, name = 'item_embedding', -- init = init_normal, W_regularizer = l2(reg_layers[0]), input_length=1) -- -+ MLP_Embedding_User = Embedding(input_dim=num_users, -+ output_dim=int(layers[0] // 2), -+ name='user_embedding', -+ embeddings_initializer='random_uniform', -+ embeddings_regularizer=l2(reg_layers[0]), -+ input_length=1) -+ MLP_Embedding_Item = Embedding(input_dim=num_items, -+ output_dim=int(layers[0] // 2), -+ name='item_embedding', -+ embeddings_initializer='random_uniform', -+ embeddings_regularizer=l2(reg_layers[0]), -+ input_length=1) - # Crucial to flatten an embedding vector! - user_latent = Flatten()(MLP_Embedding_User(user_input)) - item_latent = Flatten()(MLP_Embedding_Item(item_input)) -- -+ - # The 0-th layer is the concatenation of embedding layers -- vector = merge([user_latent, item_latent], mode = 'concat') -- -+ vector = concatenate([user_latent, item_latent]) -+ - # MLP layers -- for idx in xrange(1, num_layer): -- layer = Dense(layers[idx], W_regularizer= l2(reg_layers[idx]), activation='relu', name = 'layer%d' %idx) -+ for idx in range(1, num_layer): -+ print(idx, " : ", layers[idx]) -+ layer = Dense(layers[idx], -+ kernel_regularizer=l2(reg_layers[idx]), -+ activation='relu', -+ name='layer%d'%idx) - vector = layer(vector) -- -+ - # Final prediction layer -- prediction = Dense(1, activation='sigmoid', init='lecun_uniform', name = 'prediction')(vector) -- -- model = Model(input=[user_input, item_input], -- output=prediction) -- -+ prediction = Dense(1, -+ activation='sigmoid', -+ kernel_initializer='lecun_uniform', -+ name='prediction')(vector) -+ -+ model = Model(inputs=[user_input, item_input], -+ outputs=prediction) -+ - return model - - def get_train_instances(train, num_negatives): -@@ -97,9 +151,10 @@ def get_train_instances(train, num_negatives): - item_input.append(i) - labels.append(1) - # negative instances -- for t in xrange(num_negatives): -+ for t in range(num_negatives): - j = np.random.randint(num_items) -- while train.has_key((u, j)): -+ #while train.has_key((u, j)): -+ while (u, j) in train: - j = np.random.randint(num_items) - user_input.append(u) - item_input.append(j) -@@ -118,61 +173,73 @@ if __name__ == '__main__': - batch_size = args.batch_size - epochs = args.epochs - verbose = args.verbose -- -+ - topK = 10 - evaluation_threads = 1 #mp.cpu_count() - print("MLP arguments: %s " %(args)) -- model_out_file = 'Pretrain/%s_MLP_%s_%d.h5' %(args.dataset, args.layers, time()) -- -+ - # Loading data - t1 = time() - dataset = Dataset(args.path + args.dataset) - train, testRatings, testNegatives = dataset.trainMatrix, dataset.testRatings, dataset.testNegatives - num_users, num_items = train.shape -- print("Load data done [%.1f s]. #user=%d, #item=%d, #train=%d, #test=%d" -+ print("Load data done [%.1f s]. #user=%d, #item=%d, #train=%d, #test=%d" - %(time()-t1, num_users, num_items, train.nnz, len(testRatings))) -- -+ - # Build model - model = get_model(num_users, num_items, layers, reg_layers) -- if learner.lower() == "adagrad": -+ if learner.lower() == "adagrad": - model.compile(optimizer=Adagrad(lr=learning_rate), loss='binary_crossentropy') - elif learner.lower() == "rmsprop": - model.compile(optimizer=RMSprop(lr=learning_rate), loss='binary_crossentropy') - elif learner.lower() == "adam": - model.compile(optimizer=Adam(lr=learning_rate), loss='binary_crossentropy') - else: -- model.compile(optimizer=SGD(lr=learning_rate), loss='binary_crossentropy') -- -+ model.compile(optimizer=SGD(lr=learning_rate), loss='binary_crossentropy') -+ - # Check Init performance - t1 = time() - (hits, ndcgs) = evaluate_model(model, testRatings, testNegatives, topK, evaluation_threads) - hr, ndcg = np.array(hits).mean(), np.array(ndcgs).mean() - print('Init: HR = %.4f, NDCG = %.4f [%.1f]' %(hr, ndcg, time()-t1)) -- -+ -+ saver = tf.train.Saver() -+ - # Train model - best_hr, best_ndcg, best_iter = hr, ndcg, -1 -- for epoch in xrange(epochs): -+ for epoch in range(epochs): -+ print("Training epochs : ", epoch) - t1 = time() - # Generate training instances - user_input, item_input, labels = get_train_instances(train, num_negatives) -- -- # Training -+ -+ # Training - hist = model.fit([np.array(user_input), np.array(item_input)], #input -- np.array(labels), # labels -- batch_size=batch_size, nb_epoch=1, verbose=0, shuffle=True) -+ np.array(labels), # labels -+ batch_size=batch_size, epochs=1, verbose=0, shuffle=True) - t2 = time() - - # Evaluation - if epoch %verbose == 0: - (hits, ndcgs) = evaluate_model(model, testRatings, testNegatives, topK, evaluation_threads) - hr, ndcg, loss = np.array(hits).mean(), np.array(ndcgs).mean(), hist.history['loss'][0] -- print('Iteration %d [%.1f s]: HR = %.4f, NDCG = %.4f, loss = %.4f [%.1f s]' -+ print('Iteration %d [%.1f s]: HR = %.4f, NDCG = %.4f, loss = %.4f [%.1f s]' - % (epoch, t2-t1, hr, ndcg, loss, time()-t2)) - if hr > best_hr: - best_hr, best_ndcg, best_iter = hr, ndcg, epoch -- if args.out > 0: -- model.save_weights(model_out_file, overwrite=True) -+ # Model is trained, all epochs are done, save the golden data -+ infer_model(model, testRatings, testNegatives, topK, evaluation_threads) - - print("End. Best Iteration %d: HR = %.4f, NDCG = %.4f. " %(best_iter, best_hr, best_ndcg)) -- if args.out > 0: -- print("The best MLP model is saved to %s" %(model_out_file)) -+ # Get keras session -+ save_path = saver.save(tf.keras.backend.get_session(), './ckpts/sampleMovieLens.ckpt') -+ -+ output_node_names = "prediction/Sigmoid" -+ checkpoint_model_folder = "./ckpts/"; -+ output_graph_filename = "sampleMovieLens.pb" -+ -+ # convert checkpoints to frozen graph -+ freeze_checkpoint_graph(output_node_names, checkpoint_model_folder, output_graph_filename) -+ -+ # delete checkpoints file -+ shutil.rmtree("./ckpts") -diff --git a/evaluate.py b/evaluate.py -index 729f07a..6079a8a 100644 ---- a/evaluate.py -+++ b/evaluate.py -@@ -20,6 +20,71 @@ _testRatings = None - _testNegatives = None - _K = None - -+def infer_model(model, testRatings, testNegatives, K, num_thread): -+ """ -+ Evaluate the performance (Hit_Ratio, NDCG) of top-K recommendation -+ Return: score of each test rating. -+ """ -+ global _model -+ global _testRatings -+ global _testNegatives -+ global _K -+ _model = model -+ _testRatings = testRatings -+ _testNegatives = testNegatives -+ _K = K -+ -+ hits, ndcgs = [],[] -+ if(num_thread > 1): # Multi-thread -+ pool = multiprocessing.Pool(processes=num_thread) -+ res = pool.map(eval_one_rating, range(len(_testRatings))) -+ pool.close() -+ pool.join() -+ hits = [r[0] for r in res] -+ ndcgs = [r[1] for r in res] -+ return (hits, ndcgs) -+ -+ # open file to overwrite -+ r = open("./movielens_ratings.txt", 'w') -+ # Single thread -+ for idx in range(len(_testRatings)): -+ (hr,ndcg) = infer_one_rating(idx, r) -+ hits.append(hr) -+ ndcgs.append(ndcg) -+ return (hits, ndcgs) -+def infer_one_rating(idx, r): -+ rating = _testRatings[idx] -+ items = _testNegatives[idx] -+ u = rating[0] -+ gtItem = rating[1] -+ items.append(gtItem) -+ -+ # Get prediction scores -+ map_item_score = {} -+ users = np.full(len(items), u, dtype = 'int32') -+ predictions = _model.predict([users, np.array(items)], -+ batch_size=100, verbose=0) -+ for i in range(len(items)): -+ item = items[i] -+ map_item_score[item] = predictions[i] -+ -+ # Evaluate top rank list -+ ranklist = heapq.nlargest(_K, map_item_score, key=map_item_score.get) -+ -+ r.write("user : %s\n" % u) -+ r.write("items : %s\n" % items) -+ r.write("predicted_max_rating_item : %s\n" % ranklist[0]) -+ r.write("predicted_max_rating_prob : %s\n" % map_item_score[ranklist[0]]) -+ r.write("Top 10 Ratings:\n") -+ for i in range(len(ranklist)): -+ r.write("%s : %s\n" % (int(ranklist[i]), float(map_item_score[ranklist[i]]))) -+ r.write("#########################################################\n") -+ -+ hr = getHitRatio(ranklist, gtItem) -+ ndcg = getNDCG(ranklist, gtItem) -+ items.pop() -+ return (hr, ndcg) -+ - def evaluate_model(model, testRatings, testNegatives, K, num_thread): - """ - Evaluate the performance (Hit_Ratio, NDCG) of top-K recommendation -@@ -44,7 +109,7 @@ def evaluate_model(model, testRatings, testNegatives, K, num_thread): - ndcgs = [r[1] for r in res] - return (hits, ndcgs) - # Single thread -- for idx in xrange(len(_testRatings)): -+ for idx in range(len(_testRatings)): - (hr,ndcg) = eval_one_rating(idx) - hits.append(hr) - ndcgs.append(ndcg) -@@ -61,15 +126,15 @@ def eval_one_rating(idx): - users = np.full(len(items), u, dtype = 'int32') - predictions = _model.predict([users, np.array(items)], - batch_size=100, verbose=0) -- for i in xrange(len(items)): -+ for i in range(len(items)): - item = items[i] - map_item_score[item] = predictions[i] -- items.pop() - - # Evaluate top rank list - ranklist = heapq.nlargest(_K, map_item_score, key=map_item_score.get) - hr = getHitRatio(ranklist, gtItem) - ndcg = getNDCG(ranklist, gtItem) -+ items.pop() - return (hr, ndcg) - - def getHitRatio(ranklist, gtItem): -@@ -79,7 +144,7 @@ def getHitRatio(ranklist, gtItem): - return 0 - - def getNDCG(ranklist, gtItem): -- for i in xrange(len(ranklist)): -+ for i in range(len(ranklist)): - item = ranklist[i] - if item == gtItem: - return math.log(2) / math.log(i+2) diff --git a/samples/opensource/sampleNMT/CMakeLists.txt b/samples/opensource/sampleNMT/CMakeLists.txt index b230767f..bef83189 100644 --- a/samples/opensource/sampleNMT/CMakeLists.txt +++ b/samples/opensource/sampleNMT/CMakeLists.txt @@ -24,7 +24,6 @@ set(SAMPLE_NMT_MODEL_SOURCES model/beamSearchPolicy.cpp model/componentWeights.cpp model/contextNMT.cpp - model/debugUtil.cpp model/lstmDecoder.cpp model/lstmEncoder.cpp model/multiplicativeAlignment.cpp diff --git a/samples/opensource/sampleNMT/README.md b/samples/opensource/sampleNMT/README.md index 6e3395a1..7aae855c 100644 --- a/samples/opensource/sampleNMT/README.md +++ b/samples/opensource/sampleNMT/README.md @@ -8,7 +8,7 @@ * [Attention mechanisms](#attention-mechanisms) * [Beam search and projection](#beam-search-and-projection) * [TensorRT API layers and ops](#tensorrt-api-layers-and-ops) -- [Preparing sample data](#preparing-sample-data) +- [Prerequisites](#prerequisites) - [Running the sample](#running-the-sample) * [Sample `--help` options](#sample-help-options) - [Additional resources](#additional-resources) @@ -77,32 +77,32 @@ The Shuffle layer implements a reshape and transpose operator for tensors. As u [TopK layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#topk-layer) The TopK layer finds the top K maximum (or minimum) elements along a dimension, returning a reduced tensor and a tensor of index positions. As used in the `softmax_likelihood.cpp` file. -## Preparing sample data +## Prerequisites -1. The NMT model was trained on the [German to English (De-En) dataset](https://github.com/tensorflow/nmt#wmt-german-english) in the WMT database. Before you can run the sample, you need trained model weights and the text and vocabulary data for performing inference. +The model was trained on the [German to English (De-En) dataset](https://github.com/tensorflow/nmt#wmt-german-english) in the WMT database. Before you can run the sample, you need trained model weights and the text and vocabulary data for performing inference. - Run the following command to download the pre-trained weights, a vocabulary file and an example input text file. In addition, it will preprocess the input text file so that sampleNMT can translate it. - ```bash - export TRT_DATADIR=/usr/src/tensorrt/data - pushd /tmp - $TRT_OSSPATH/samples/opensource/sampleNMT/get_newstest2015.sh - mkdir -p $TRT_DATADIR/nmt && mv data/nmt/* $TRT_DATADIR/nmt/ - popd - ``` +Run the following command from the ``. This will download the pre-trained weights, a vocabulary file and an example input text file. In addition, it will preprocess the input text file so that sampleNMT can translate it. The following command prepares all necessary input data. +`./samples/sampleNMT/get_newstest2015.sh` ## Running the sample Now that you have trained weights, downloaded the text and vocabulary data, and compiled the sample you can run the sample. -1. Compile the sample by following build instructions in [TensorRT README](https://github.com/NVIDIA/TensorRT/). +1. Compile this sample by running `make` in the `/samples/sampleNMT` directory. The binary named `sample_nmt` will be created in the `/bin` directory. + ``` + cd /samples/sampleNMT + make + ``` -2. Run the sample to generate the example translation from German to English: - ```bash + Where `` is where you installed TensorRT. + +2. Run the sample to generate the example translation from German to English: + ``` sample_nmt --data_writer=text ``` - **NOTE:** If your data is not located in `/data/samples/nmt/deen`, use the `--data_dir=` option. Where `` is the path to your data directory. For example: - ```bash + **Note:** If your data is not located in `/data/samples/nmt/deen`, use the `--data_dir=` option. Where `` is the path to your data directory. For example: + ``` sample_nmt --data_dir= --data_writer=text ``` @@ -110,14 +110,14 @@ Now that you have trained weights, downloaded the text and vocabulary data, and The translated output is located in the `./translation_output.txt` file. -3. Run the sample to get the BLEU score (the quality of the translated text) for the first 100 sentences: - ```bash +3. Run the sample to get the BLEU score (the quality of the translated text) for the first 100 sentences: + ``` sample_nmt --max_inference_samples=100 --data-writer=bleu ``` -4. Verify your translated output. - a. Compare your translated output to the `$TRT_DATADIR/data/newstest2015.tok.bpe.32000.en` translated output file in the TensorRT package. - b. Compare the quality of your translated output with the 25.85 BLEU score quality metric file in the TensorRT package. +4. Verify your translated output. + a. Compare your translated output to the `/data/newstest2015.tok.bpe.32000.en` translated output file in the TensorRT package. + b. Compare the quality of your translated output with the 25.85 BLEU score quality metric file in the TensorRT package. ### Sample `--help` options diff --git a/samples/opensource/sampleNMT/chptToBin.py b/samples/opensource/sampleNMT/chptToBin.py index 94de4620..44612968 100644 --- a/samples/opensource/sampleNMT/chptToBin.py +++ b/samples/opensource/sampleNMT/chptToBin.py @@ -22,14 +22,14 @@ import argparse from copy import deepcopy """ - The conversion of a checkpoint from - https://github.com/tensorflow/nmt project + The conversion of a checkpoint from + https://github.com/tensorflow/nmt project The conversion was tested using Tensorflow 1.6 """ def chpt_to_dict_arrays_simple(file_name): """ - Convert a checkpoint into into a dictionary of numpy arrays + Convert a checkpoint into into a dictionary of numpy arrays for later use in TensorRT NMT sample. """ config = tf.ConfigProto(allow_soft_placement=True) @@ -53,7 +53,7 @@ def chpt_to_dict_arrays_simple(file_name): def chpt_to_dict_arrays(): """ - Convert a checkpoint into a dictionary of numpy arrays + Convert a checkpoint into a dictionary of numpy arrays for later use in TensorRT NMT sample. git clone https://github.com/tensorflow/nmt.git """ @@ -132,7 +132,7 @@ def concatenate_layers(params): if bi_layers == 1: bifw_encoder_prefix = u'dynamic_seq2seq/encoder/bidirectional_rnn/fw/basic_lstm_cell/' bibw_encoder_prefix = u'dynamic_seq2seq/encoder/bidirectional_rnn/bw/basic_lstm_cell/' - data["encrnnkernel"] = params[bifw_encoder_prefix + kernel_alias] + data["encrnnkernel"] = params[bifw_encoder_prefix + kernel_alias] tmp_weights = params[bibw_encoder_prefix + kernel_alias] data["encrnnkernel"] = np.concatenate((data["encrnnkernel"], tmp_weights), axis=0) @@ -188,8 +188,8 @@ def concatenate_layers(params): num_units = int(data["decrnnkernel"].shape[1] / 4) encoder_type_int = 1 if encoder_type == 'bidirectional' else 0 - dimensions = {"layers": layers, - "encoder_type": encoder_type_int, + dimensions = {"layers": layers, + "encoder_type": encoder_type_int, "num_units": num_units, "encembed_outputs": data['encembed'].shape[0], "decembed_outputs": data['decembed'].shape[0], @@ -197,7 +197,7 @@ def concatenate_layers(params): return dimensions, data def convert_rnn_kernel(weights, dimensions, is_decoder_rnn = False): - """ + """ In place. weights conversion TensorFlow weight parameters for BasicLSTMCell are formatted as: @@ -219,7 +219,7 @@ def convert_rnn_kernel(weights, dimensions, is_decoder_rnn = False): CellN: Wf, Rf, Wi, Ri, Wc, Rc, Wo, Ro, Empty states Update: alternative notation - Tensorflow documents gates' order in e.g. + Tensorflow documents gates' order in e.g. https:github.com/tensorflow/tensorflow/blob/r1.4/tensorflow/python/ops/rnn_cell_impl.py:439 TF: i = input_gate, j = new_input (cell gate), f = forget_gate, o = output_gate - ijfo Need to convert 'ijfo' to 'fijo' @@ -241,7 +241,7 @@ def convert_rnn_kernel(weights, dimensions, is_decoder_rnn = False): weights = np.reshape(weights, (layers, 2, input_size, 4, num_units)) print("After reshape: {0}".format(weights.shape)) - # reorder/transpose axis to match TensorRT format (layers, 2, 4, num_units, input_size) + # reorder/transpose axis to match TensorRT format (layers, 2, 4, num_units, input_size) weights = np.moveaxis(weights, [2, 3, 4], [4, 2, 3]) print("After moveaxis: {0}".format(weights.shape)) @@ -313,24 +313,24 @@ def convert_rnn_bias(weights, dimensions, forget_bias = 1.0): def convert_weigts(dimensions, data, forget_bias = 1.0): """Convert weights from Tensorflow to TensorRT format""" - - print("Processing encoder RNN kernel") + + print("Processing encoder RNN kernel") data["encrnnkernel"] = convert_rnn_kernel(data["encrnnkernel"], dimensions, False) - + print("Processing encoder RNN bias") data["encrnnbias"] = convert_rnn_bias(data["encrnnbias"], dimensions, forget_bias = forget_bias) - print("Processing decoder RNN kernel") + print("Processing decoder RNN kernel") data["decrnnkernel"] = convert_rnn_kernel(data["decrnnkernel"], dimensions, True) print("Processing decoder RNN bias") data["decrnnbias"] = convert_rnn_bias(data["decrnnbias"], dimensions, forget_bias = forget_bias) - + return data def save_layer_weights(data, list_keys, dims, footer_string, file_name): """ - data - dictionary with string names as keys and + data - dictionary with string names as keys and numpy weights as values list_keys - list of dictionary keys to save dims - list of int values relevant to the layer @@ -362,7 +362,7 @@ def main(_): print ('python {0} --weightsdir='.format(sys.argv[0])) print ("""e.g. \npython {0} --src=en --tgt=vi \\ --ckpt=/path/to/envi_model/translate.ckpt \\ - --hparams_path=nmt/standard_hparams/iwslt15.json \\ + --hparams_path=nmt/standard_hparams/iwslt15.json \\ --out_dir=/tmp/envi \\ --vocab_prefix=/tmp/nmt_data/vocab \\ --inference_input_file=/tmp/nmt_data/tst2013.en \\ @@ -387,7 +387,7 @@ def main(_): params = chpt_to_dict_arrays(trt_flags.metafile) print('\nLoading the checkpoint...\n') - + print('\nConcatenating the weights...') dimensions, data = concatenate_layers(params) @@ -435,7 +435,7 @@ def main(_): [ dimensions["num_units"], \ dimensions["num_units"] ], \ trt_string, case_dir + "decmem.bin") - + #decattkernel # first dimension is 3 * num_units of bi RNN, 2 * num_units otherwise save_layer_weights(data, ["decattkernel"], \ diff --git a/samples/opensource/sampleNMT/cudaError.h b/samples/opensource/sampleNMT/cudaError.h index bc9cab85..dd0aee12 100644 --- a/samples/opensource/sampleNMT/cudaError.h +++ b/samples/opensource/sampleNMT/cudaError.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef SAMPLE_NMT_CUDA_ERROR_ #define SAMPLE_NMT_CUDA_ERROR_ diff --git a/samples/opensource/sampleNMT/data/benchmarkWriter.h b/samples/opensource/sampleNMT/data/benchmarkWriter.h index 83d47496..6abad33e 100644 --- a/samples/opensource/sampleNMT/data/benchmarkWriter.h +++ b/samples/opensource/sampleNMT/data/benchmarkWriter.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef SAMPLE_NMT_BENCHMARK_WRITER_ #define SAMPLE_NMT_BENCHMARK_WRITER_ diff --git a/samples/opensource/sampleNMT/data/bleuScoreWriter.cpp b/samples/opensource/sampleNMT/data/bleuScoreWriter.cpp index d65a7b5f..db78c2a4 100644 --- a/samples/opensource/sampleNMT/data/bleuScoreWriter.cpp +++ b/samples/opensource/sampleNMT/data/bleuScoreWriter.cpp @@ -15,10 +15,10 @@ */ #include "bleuScoreWriter.h" +#include "common.h" #include "logger.h" #include -#include #include #include #include @@ -53,7 +53,7 @@ int read(std::vector& samples, std::shared_ptr input, i #else p0 = line.find("\u2581"); #endif - assert((p0 == std::string::npos)); + ASSERT((p0 == std::string::npos)); std::istringstream ss(line); std::string token; tokens.resize(0); @@ -120,7 +120,7 @@ void accumulateBLEU(const std::vector& referenceSamples, const std::v int maxOrder, size_t& referenceLength, size_t& translationLength, std::vector& matchesByOrder, std::vector& possibleMatchesByOrder) { - assert(referenceSamples.size() == outputSamples.size()); + ASSERT(referenceSamples.size() == outputSamples.size()); auto reference = referenceSamples.begin(); auto translation = outputSamples.begin(); @@ -165,7 +165,7 @@ void BLEUScoreWriter::write(const int* hOutputData, int actualOutputSequenceLeng std::vector outputSamples; std::vector referenceSamples; int numReferenceSamples = read(referenceSamples, mReferenceInput, 1); - assert(numReferenceSamples == 1); + ASSERT(numReferenceSamples == 1); Segment_t segment; std::stringstream filteredSentence(DataWriter::generateText(actualOutputSequenceLength, hOutputData, mVocabulary)); diff --git a/samples/opensource/sampleNMT/data/dataWriter.cpp b/samples/opensource/sampleNMT/data/dataWriter.cpp index 214af256..4e29096c 100644 --- a/samples/opensource/sampleNMT/data/dataWriter.cpp +++ b/samples/opensource/sampleNMT/data/dataWriter.cpp @@ -51,4 +51,4 @@ std::string DataWriter::generateText(int sequenceLength, const int* currentOutpu } return sentence.str(); } -} // namespace nmtSample +} // namespace nmtSample \ No newline at end of file diff --git a/samples/opensource/sampleNMT/data/dataWriter.h b/samples/opensource/sampleNMT/data/dataWriter.h index bbcd130d..bb3a4444 100644 --- a/samples/opensource/sampleNMT/data/dataWriter.h +++ b/samples/opensource/sampleNMT/data/dataWriter.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef SAMPLE_NMT_DATA_WRITER_ #define SAMPLE_NMT_DATA_WRITER_ diff --git a/samples/opensource/sampleNMT/data/limitedSamplesDataReader.cpp b/samples/opensource/sampleNMT/data/limitedSamplesDataReader.cpp index ad6f46d3..4d51a887 100644 --- a/samples/opensource/sampleNMT/data/limitedSamplesDataReader.cpp +++ b/samples/opensource/sampleNMT/data/limitedSamplesDataReader.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "limitedSamplesDataReader.h" #include diff --git a/samples/opensource/sampleNMT/data/limitedSamplesDataReader.h b/samples/opensource/sampleNMT/data/limitedSamplesDataReader.h index ff52c621..0378650f 100644 --- a/samples/opensource/sampleNMT/data/limitedSamplesDataReader.h +++ b/samples/opensource/sampleNMT/data/limitedSamplesDataReader.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef SAMPLE_NMT_LIMITED_SAMPLES_DATA_READER_ #define SAMPLE_NMT_LIMITED_SAMPLES_DATA_READER_ diff --git a/samples/opensource/sampleNMT/data/textReader.h b/samples/opensource/sampleNMT/data/textReader.h index a52af05f..5f5786dd 100644 --- a/samples/opensource/sampleNMT/data/textReader.h +++ b/samples/opensource/sampleNMT/data/textReader.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef SAMPLE_NMT_TEXT_READER_ #define SAMPLE_NMT_TEXT_READER_ diff --git a/samples/opensource/sampleNMT/data/vocabulary.cpp b/samples/opensource/sampleNMT/data/vocabulary.cpp index d7c10992..7de9867c 100644 --- a/samples/opensource/sampleNMT/data/vocabulary.cpp +++ b/samples/opensource/sampleNMT/data/vocabulary.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ +#include "common.h" #include "vocabulary.h" -#include #include #include #include @@ -33,7 +33,7 @@ Vocabulary::Vocabulary() void Vocabulary::add(const std::string& token) { - assert(mTokenToId.find(token) == mTokenToId.end()); + ASSERT(mTokenToId.find(token) == mTokenToId.end()); mTokenToId[token] = mNumTokens; mIdToToken.push_back(token); mNumTokens++; @@ -49,7 +49,7 @@ int Vocabulary::getId(const std::string& token) const std::string Vocabulary::getToken(int id) const { - assert(id < mNumTokens); + ASSERT(id < mNumTokens); return mIdToToken[id]; } @@ -71,19 +71,19 @@ std::istream& operator>>(std::istream& input, Vocabulary& value) { auto it = value.mTokenToId.find(Vocabulary::mSosStr); - assert(it != value.mTokenToId.end()); + ASSERT(it != value.mTokenToId.end()); value.mSosId = it->second; } { auto it = value.mTokenToId.find(Vocabulary::mEosStr); - assert(it != value.mTokenToId.end()); + ASSERT(it != value.mTokenToId.end()); value.mEosId = it->second; } { auto it = value.mTokenToId.find(Vocabulary::mUnkStr); - assert(it != value.mTokenToId.end()); + ASSERT(it != value.mTokenToId.end()); value.mUnkId = it->second; } diff --git a/samples/opensource/sampleNMT/get_newstest2015.sh b/samples/opensource/sampleNMT/get_newstest2015.sh index 8de80b4e..b42cca22 100755 --- a/samples/opensource/sampleNMT/get_newstest2015.sh +++ b/samples/opensource/sampleNMT/get_newstest2015.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Copyright 2017 Google Inc. -# Modifications Copyright (c) 2021, NVIDIA CORPORATION. +# Modifications Copyright (c) 2021 Nvidia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -48,7 +48,7 @@ mkdir -p ${DATA_DIR} if [ ! -f ${OUTPUT_DIR}/sampleNMT_data.tar.bz2 ]; then echo "Downloading sample_nmt support data..." curl -o ${OUTPUT_DIR}/sampleNMT_data.tar.bz2 \ - https://developer.download.nvidia.com/compute/machine-learning/tensorrt/models/sampleNMT_data.tar.bz2 + https://developer.download.nvidia.com/compute/machine-learning/tensorrt/models/sampleNMT_data.tar.bz2 fi echo "Extracting sample_nmt support data..." @@ -99,4 +99,3 @@ bpe_de=${DATA_DIR}/newstest2015.tok.bpe.32000.de bpe_en=${DATA_DIR}/newstest2015.tok.bpe.32000.en split_subwords ${tok_de} ${bpe_de} split_subwords ${tok_en} ${bpe_en} - diff --git a/samples/opensource/sampleNMT/model/beamSearchPolicy.cpp b/samples/opensource/sampleNMT/model/beamSearchPolicy.cpp index 8265cbae..e704f178 100644 --- a/samples/opensource/sampleNMT/model/beamSearchPolicy.cpp +++ b/samples/opensource/sampleNMT/model/beamSearchPolicy.cpp @@ -15,12 +15,12 @@ */ #include "beamSearchPolicy.h" +#include "common.h" #ifdef _MSC_VER // Macro definition needed to avoid name collision with std::min/max and Windows.h min/max #define NOMINMAX #endif #include -#include #include #include @@ -142,7 +142,7 @@ void BeamSearchPolicy::readGeneratedResult( else { // We don't have a finished sequence generated, will output the unfinished one with the highest likelihood - assert(mValidSamples[sampleId]); + ASSERT(mValidSamples[sampleId]); backtrack(mTimestepId - 1, sampleId, 0, hOutputData + sampleId * maxOutputSequenceLength, maxOutputSequenceLength - 1); hActualOutputSequenceLengths[sampleId] = mTimestepId; diff --git a/samples/opensource/sampleNMT/model/componentWeights.cpp b/samples/opensource/sampleNMT/model/componentWeights.cpp index cb223650..71c54152 100644 --- a/samples/opensource/sampleNMT/model/componentWeights.cpp +++ b/samples/opensource/sampleNMT/model/componentWeights.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ +#include "common.h" #include "componentWeights.h" -#include #include namespace nmtSample @@ -34,7 +34,7 @@ std::istream& operator>>(std::istream& input, ComponentWeights& value) size_t metaDataCount = ((int32_t*) footer)[0]; std::string str(footer + sizeof(int32_t), footer + footerSize); - assert(footerString.compare(str) == 0); + ASSERT(footerString.compare(str) == 0); free(footer); input.seekg(-(footerSize + metaDataCount * sizeof(int32_t)), std::ios::end); diff --git a/samples/opensource/sampleNMT/model/componentWeights.h b/samples/opensource/sampleNMT/model/componentWeights.h index cd36babc..87b6ad1b 100644 --- a/samples/opensource/sampleNMT/model/componentWeights.h +++ b/samples/opensource/sampleNMT/model/componentWeights.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef SAMPLE_NMT_COMPONENT_WEIGHTS_ #define SAMPLE_NMT_COMPONENT_WEIGHTS_ diff --git a/samples/opensource/sampleNMT/model/contextNMT.cpp b/samples/opensource/sampleNMT/model/contextNMT.cpp index e2002476..3025243e 100644 --- a/samples/opensource/sampleNMT/model/contextNMT.cpp +++ b/samples/opensource/sampleNMT/model/contextNMT.cpp @@ -14,9 +14,8 @@ * limitations under the License. */ +#include "common.h" #include "contextNMT.h" - -#include #include namespace nmtSample @@ -25,16 +24,17 @@ void Context::addToModel(nvinfer1::INetworkDefinition* network, nvinfer1::ITenso nvinfer1::ITensor* memoryStates, nvinfer1::ITensor* alignmentScores, nvinfer1::ITensor** contextOutput) { auto raggedSoftmaxLayer = network->addRaggedSoftMax(*alignmentScores, *actualInputSequenceLengths); - assert(raggedSoftmaxLayer != nullptr); + ASSERT(raggedSoftmaxLayer != nullptr); raggedSoftmaxLayer->setName("Context Ragged Softmax"); auto softmaxTensor = raggedSoftmaxLayer->getOutput(0); - assert(softmaxTensor != nullptr); + ASSERT(softmaxTensor != nullptr); - auto mmLayer = network->addMatrixMultiply(*softmaxTensor, false, *memoryStates, false); - assert(mmLayer != nullptr); + auto mmLayer + = network->addMatrixMultiply(*softmaxTensor, MatrixOperation::kNONE, *memoryStates, MatrixOperation::kNONE); + ASSERT(mmLayer != nullptr); mmLayer->setName("Context Matrix Multiply"); *contextOutput = mmLayer->getOutput(0); - assert(*contextOutput != nullptr); + ASSERT(*contextOutput != nullptr); } std::string Context::getInfo() diff --git a/samples/opensource/sampleNMT/model/debugUtil.cpp b/samples/opensource/sampleNMT/model/debugUtil.cpp deleted file mode 100644 index 8cd8fd60..00000000 --- a/samples/opensource/sampleNMT/model/debugUtil.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. - * - * 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 "debugUtil.h" - -#include -#include - -#include "../cudaError.h" - -namespace nmtSample -{ -std::list DebugUtil::mPlugins; - -DebugUtil::DumpTensorPlugin::DumpTensorPlugin(std::shared_ptr out) - : mOut(out) -{ -} - -int DebugUtil::DumpTensorPlugin::getNbOutputs() const -{ - return 1; -} - -nvinfer1::Dims DebugUtil::DumpTensorPlugin::getOutputDimensions( - int index, const nvinfer1::Dims* inputs, int nbInputDims) -{ - return inputs[0]; -} - -void DebugUtil::DumpTensorPlugin::configure( - const nvinfer1::Dims* inputDims, int nbInputs, const nvinfer1::Dims* outputDims, int nbOutputs, int maxBatchSize) -{ - mDims = inputDims[0]; - - *mOut << "Max batch size = " << maxBatchSize << std::endl; - *mOut << "Tensor dimensions = "; - mTensorVolume = 1; - for (int i = 0; i < mDims.nbDims; ++i) - { - if (i > 0) - *mOut << "x"; - *mOut << mDims.d[i]; - mTensorVolume *= mDims.d[i]; - } - mElemsPerRow = 1; - for (int i = mDims.nbDims - 1; i >= 0; --i) - { - if (mElemsPerRow == 1) - mElemsPerRow *= mDims.d[i]; - } - *mOut << std::endl; - - mData = std::make_shared>(mTensorVolume * maxBatchSize); -} - -int DebugUtil::DumpTensorPlugin::initialize() -{ - return 0; -} - -void DebugUtil::DumpTensorPlugin::terminate() -{ - mOut.reset(); - mData.reset(); -} - -size_t DebugUtil::DumpTensorPlugin::getWorkspaceSize(int maxBatchSize) const -{ - return 0; -} - -int DebugUtil::DumpTensorPlugin::enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) -{ - int totalElems = batchSize * mTensorVolume; - - CUDA_CHECK(cudaMemcpyAsync(*mData, inputs[0], totalElems * sizeof(float), cudaMemcpyDeviceToHost, stream)); - CUDA_CHECK(cudaStreamSynchronize(stream)); - CUDA_CHECK(cudaMemcpyAsync(outputs[0], inputs[0], totalElems * sizeof(float), cudaMemcpyDeviceToDevice, stream)); - - *mOut << "Batch size = " << batchSize << "\n"; - int rowCount = totalElems / mElemsPerRow; - for (int rowId = 0; rowId < rowCount; ++rowId) - { - for (int i = 0; i < mElemsPerRow; ++i) - { - if (i > 0) - *mOut << " "; - *mOut << (*mData)[rowId * mElemsPerRow + i]; - } - *mOut << "\n"; - } - *mOut << std::endl; - - return 0; -} - -size_t DebugUtil::DumpTensorPlugin::getSerializationSize() -{ - assert(0); - return 0; -} - -void DebugUtil::DumpTensorPlugin::serialize(void* buffer) -{ - assert(0); -} - -void DebugUtil::addDumpTensorToStream(nvinfer1::INetworkDefinition* network, nvinfer1::ITensor* input, - nvinfer1::ITensor** output, std::shared_ptr out) -{ - assert(!input->getBroadcastAcrossBatch()); - auto plugin = std::make_shared(out); - nvinfer1::ITensor* inputTensors[] = {input}; - auto pluginLayer = network->addPlugin(inputTensors, 1, *plugin); - assert(pluginLayer != nullptr); - *output = pluginLayer->getOutput(0); - assert(*output != nullptr); - mPlugins.push_back(plugin); -} -} // namespace nmtSample diff --git a/samples/opensource/sampleNMT/model/debugUtil.h b/samples/opensource/sampleNMT/model/debugUtil.h deleted file mode 100644 index 99c290ac..00000000 --- a/samples/opensource/sampleNMT/model/debugUtil.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. - * - * 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. - */ - -#ifndef SAMPLE_NMT_DEBUG_UTIL_ -#define SAMPLE_NMT_DEBUG_UTIL_ - -#include "NvInfer.h" - -#include -#include -#include - -#include "../pinnedHostBuffer.h" - -namespace nmtSample -{ -/** \class DebugUtil - * - * \brief container for static debug utility functions - * - */ -class DebugUtil -{ -private: - class DumpTensorPlugin : public nvinfer1::IPlugin - { - public: - typedef std::shared_ptr ptr; - - DumpTensorPlugin(std::shared_ptr out); - - ~DumpTensorPlugin() override = default; - - int getNbOutputs() const override; - - nvinfer1::Dims getOutputDimensions(int index, const nvinfer1::Dims* inputs, int nbInputDims) override; - - void configure(const nvinfer1::Dims* inputDims, int nbInputs, const nvinfer1::Dims* outputDims, int nbOutputs, - int maxBatchSize) override; - - int initialize() override; - - void terminate() override; - - size_t getWorkspaceSize(int maxBatchSize) const override; - - int enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) override; - - size_t getSerializationSize() override; - - void serialize(void* buffer) override; - - private: - std::shared_ptr mOut; - nvinfer1::Dims mDims; - int mTensorVolume; - int mElemsPerRow; - PinnedHostBuffer::ptr mData; - }; - -public: - static void addDumpTensorToStream(nvinfer1::INetworkDefinition* network, nvinfer1::ITensor* input, - nvinfer1::ITensor** output, std::shared_ptr out); - -private: - static std::list mPlugins; -}; -} // namespace nmtSample - -#endif // SAMPLE_NMT_DEBUG_UTIL_ diff --git a/samples/opensource/sampleNMT/model/lstmDecoder.cpp b/samples/opensource/sampleNMT/model/lstmDecoder.cpp index 46d30f92..c4883bb8 100644 --- a/samples/opensource/sampleNMT/model/lstmDecoder.cpp +++ b/samples/opensource/sampleNMT/model/lstmDecoder.cpp @@ -14,14 +14,10 @@ * limitations under the License. */ +#include "common.h" #include "lstmDecoder.h" - #include "trtUtil.h" - -#include "debugUtil.h" #include - -#include #include namespace nmtSample @@ -30,9 +26,9 @@ LSTMDecoder::LSTMDecoder(ComponentWeights::ptr weights) : mWeights(weights) { // please refer to chpt_to_bin.py for the details on the format - assert(mWeights->mMetaData.size() >= 4); + ASSERT(mWeights->mMetaData.size() >= 4); nvinfer1::DataType dataType = static_cast(mWeights->mMetaData[0]); - assert(dataType == nvinfer1::DataType::kFLOAT); + ASSERT(dataType == nvinfer1::DataType::kFLOAT); mRNNKind = mWeights->mMetaData[1]; mNumLayers = mWeights->mMetaData[2]; mNumUnits = mWeights->mMetaData[3]; @@ -58,7 +54,7 @@ LSTMDecoder::LSTMDecoder(ComponentWeights::ptr weights) biasOffset = biasOffset + mNumUnits * elementSize; } } - assert(kernelOffset + biasOffset - biasStartOffset == mWeights->mWeights.size()); + ASSERT(kernelOffset + biasOffset - biasStartOffset == mWeights->mWeights.size()); } void LSTMDecoder::addToModel(nvinfer1::INetworkDefinition* network, nvinfer1::ITensor* inputEmbeddedData, @@ -68,27 +64,24 @@ void LSTMDecoder::addToModel(nvinfer1::INetworkDefinition* network, nvinfer1::IT int inputWidth; { auto dims = inputEmbeddedData->getDimensions(); - assert(dims.nbDims == 2); - assert(dims.type[0] == nvinfer1::DimensionType::kINDEX); + ASSERT(dims.nbDims == 2); beamWidth = dims.d[0]; - assert(dims.type[1] == nvinfer1::DimensionType::kCHANNEL); inputWidth = dims.d[1]; } nvinfer1::ITensor* shuffledInput; { auto shuffleLayer = network->addShuffle(*inputEmbeddedData); - assert(shuffleLayer != nullptr); + ASSERT(shuffleLayer != nullptr); shuffleLayer->setName("Reshape input for LSTM decoder"); - nvinfer1::Dims shuffleDims{3, {beamWidth, 1, inputWidth}, - {nvinfer1::DimensionType::kINDEX, nvinfer1::DimensionType::kSEQUENCE, nvinfer1::DimensionType::kCHANNEL}}; + nvinfer1::Dims shuffleDims{3, {beamWidth, 1, inputWidth}}; shuffleLayer->setReshapeDimensions(shuffleDims); shuffledInput = shuffleLayer->getOutput(0); - assert(shuffledInput != nullptr); + ASSERT(shuffledInput != nullptr); } auto decoderLayer = network->addRNNv2(*shuffledInput, mNumLayers, mNumUnits, 1, nvinfer1::RNNOperation::kLSTM); - assert(decoderLayer != nullptr); + ASSERT(decoderLayer != nullptr); decoderLayer->setName("LSTM decoder"); decoderLayer->setInputMode(nvinfer1::RNNInputMode::kLINEAR); @@ -107,35 +100,32 @@ void LSTMDecoder::addToModel(nvinfer1::INetworkDefinition* network, nvinfer1::IT decoderLayer->setHiddenState(*inputStates[0]); decoderLayer->setCellState(*inputStates[1]); *outputData = decoderLayer->getOutput(0); - assert(*outputData != nullptr); + ASSERT(*outputData != nullptr); { auto shuffleLayer = network->addShuffle(**outputData); - assert(shuffleLayer != nullptr); + ASSERT(shuffleLayer != nullptr); shuffleLayer->setName("Reshape output from LSTM decoder"); - nvinfer1::Dims shuffleDims{ - 2, {beamWidth, mNumUnits}, {nvinfer1::DimensionType::kINDEX, nvinfer1::DimensionType::kCHANNEL}}; + nvinfer1::Dims shuffleDims{2, {beamWidth, mNumUnits}}; shuffleLayer->setReshapeDimensions(shuffleDims); auto shuffledOutput = shuffleLayer->getOutput(0); - assert(shuffledOutput != nullptr); + ASSERT(shuffledOutput != nullptr); *outputData = shuffledOutput; } // Per layer hidden output outputStates[0] = decoderLayer->getOutput(1); - assert(outputStates[0] != nullptr); + ASSERT(outputStates[0] != nullptr); // Per layer cell output outputStates[1] = decoderLayer->getOutput(2); - assert(outputStates[1] != nullptr); + ASSERT(outputStates[1] != nullptr); } std::vector LSTMDecoder::getStateSizes() { - nvinfer1::Dims hiddenStateDims{ - 2, {mNumLayers, mNumUnits}, {nvinfer1::DimensionType::kSPATIAL, nvinfer1::DimensionType::kCHANNEL}}; - nvinfer1::Dims cellStateDims{ - 2, {mNumLayers, mNumUnits}, {nvinfer1::DimensionType::kSPATIAL, nvinfer1::DimensionType::kCHANNEL}}; + nvinfer1::Dims hiddenStateDims{2, {mNumLayers, mNumUnits}}; + nvinfer1::Dims cellStateDims{2, {mNumLayers, mNumUnits}}; return std::vector({hiddenStateDims, cellStateDims}); } diff --git a/samples/opensource/sampleNMT/model/lstmEncoder.cpp b/samples/opensource/sampleNMT/model/lstmEncoder.cpp index deb0f728..33565e1a 100644 --- a/samples/opensource/sampleNMT/model/lstmEncoder.cpp +++ b/samples/opensource/sampleNMT/model/lstmEncoder.cpp @@ -14,10 +14,9 @@ * limitations under the License. */ +#include "common.h" #include "lstmEncoder.h" #include "trtUtil.h" - -#include #include namespace nmtSample @@ -27,9 +26,9 @@ LSTMEncoder::LSTMEncoder(ComponentWeights::ptr weights) : mWeights(weights) { // please refer to chpt_to_bin.py for the details on the format - assert(mWeights->mMetaData.size() >= 4); + ASSERT(mWeights->mMetaData.size() >= 4); const nvinfer1::DataType dataType = static_cast(mWeights->mMetaData[0]); - assert(dataType == nvinfer1::DataType::kFLOAT); + ASSERT(dataType == nvinfer1::DataType::kFLOAT); mRNNKind = mWeights->mMetaData[1]; mNumLayers = mWeights->mMetaData[2]; mNumUnits = mWeights->mMetaData[3]; @@ -54,7 +53,7 @@ LSTMEncoder::LSTMEncoder(ComponentWeights::ptr weights) biasOffset = biasOffset + mNumUnits * elementSize; } } - assert(kernelOffset + biasOffset - biasStartOffset == mWeights->mWeights.size()); + ASSERT(kernelOffset + biasOffset - biasStartOffset == mWeights->mWeights.size()); } void LSTMEncoder::addToModel(nvinfer1::INetworkDefinition* network, int maxInputSequenceLength, @@ -63,7 +62,7 @@ void LSTMEncoder::addToModel(nvinfer1::INetworkDefinition* network, int maxInput { auto encoderLayer = network->addRNNv2( *inputEmbeddedData, mNumLayers, mNumUnits, maxInputSequenceLength, nvinfer1::RNNOperation::kLSTM); - assert(encoderLayer != nullptr); + ASSERT(encoderLayer != nullptr); encoderLayer->setName("LSTM encoder"); encoderLayer->setSequenceLengths(*actualInputSequenceLengths); @@ -83,17 +82,17 @@ void LSTMEncoder::addToModel(nvinfer1::INetworkDefinition* network, int maxInput encoderLayer->setHiddenState(*inputStates[0]); encoderLayer->setCellState(*inputStates[1]); *memoryStates = encoderLayer->getOutput(0); - assert(*memoryStates != nullptr); + ASSERT(*memoryStates != nullptr); if (lastTimestepStates) { // Per layer hidden output lastTimestepStates[0] = encoderLayer->getOutput(1); - assert(lastTimestepStates[0] != nullptr); + ASSERT(lastTimestepStates[0] != nullptr); // Per layer cell output lastTimestepStates[1] = encoderLayer->getOutput(2); - assert(lastTimestepStates[1] != nullptr); + ASSERT(lastTimestepStates[1] != nullptr); } } @@ -104,10 +103,8 @@ int LSTMEncoder::getMemoryStatesSize() std::vector LSTMEncoder::getStateSizes() { - nvinfer1::Dims hiddenStateDims{ - 2, {mNumLayers, mNumUnits}, {nvinfer1::DimensionType::kSPATIAL, nvinfer1::DimensionType::kCHANNEL}}; - nvinfer1::Dims cellStateDims{ - 2, {mNumLayers, mNumUnits}, {nvinfer1::DimensionType::kSPATIAL, nvinfer1::DimensionType::kCHANNEL}}; + nvinfer1::Dims hiddenStateDims{2, {mNumLayers, mNumUnits}}; + nvinfer1::Dims cellStateDims{2, {mNumLayers, mNumUnits}}; return std::vector({hiddenStateDims, cellStateDims}); } diff --git a/samples/opensource/sampleNMT/model/multiplicativeAlignment.cpp b/samples/opensource/sampleNMT/model/multiplicativeAlignment.cpp index 02cf3911..4847aaaf 100644 --- a/samples/opensource/sampleNMT/model/multiplicativeAlignment.cpp +++ b/samples/opensource/sampleNMT/model/multiplicativeAlignment.cpp @@ -14,9 +14,8 @@ * limitations under the License. */ +#include "common.h" #include "multiplicativeAlignment.h" - -#include #include namespace nmtSample @@ -25,9 +24,9 @@ MultiplicativeAlignment::MultiplicativeAlignment(ComponentWeights::ptr weights) : mWeights(weights) { // please refer to chpt_to_bin.py for the details on the format - assert(mWeights->mMetaData.size() >= 3); + ASSERT(mWeights->mMetaData.size() >= 3); mKernelWeights.type = static_cast(mWeights->mMetaData[0]); - assert(mKernelWeights.type == nvinfer1::DataType::kFLOAT); + ASSERT(mKernelWeights.type == nvinfer1::DataType::kFLOAT); mInputChannelCount = mWeights->mMetaData[1]; mOutputChannelCount = mWeights->mMetaData[2]; @@ -38,29 +37,29 @@ MultiplicativeAlignment::MultiplicativeAlignment(ComponentWeights::ptr weights) void MultiplicativeAlignment::addToModel(nvinfer1::INetworkDefinition* network, nvinfer1::ITensor* attentionKeys, nvinfer1::ITensor* queryStates, nvinfer1::ITensor** alignmentScores) { - auto mmLayer = network->addMatrixMultiply(*queryStates, false, *attentionKeys, true); - assert(mmLayer != nullptr); + auto mmLayer + = network->addMatrixMultiply(*queryStates, MatrixOperation::kNONE, *attentionKeys, MatrixOperation::kTRANSPOSE); + ASSERT(mmLayer != nullptr); mmLayer->setName("Raw Alignment Scores MM (Queries x Keys) in multiplicative attention"); *alignmentScores = mmLayer->getOutput(0); - assert(*alignmentScores != nullptr); + ASSERT(*alignmentScores != nullptr); } void MultiplicativeAlignment::addAttentionKeys( nvinfer1::INetworkDefinition* network, nvinfer1::ITensor* memoryStates, nvinfer1::ITensor** attentionKeys) { - nvinfer1::Dims weightDims{2, {mInputChannelCount, mOutputChannelCount}, - {nvinfer1::DimensionType::kCHANNEL, nvinfer1::DimensionType::kCHANNEL}}; + nvinfer1::Dims weightDims{2, {mInputChannelCount, mOutputChannelCount}}; auto constLayer = network->addConstant(weightDims, mKernelWeights); - assert(constLayer != nullptr); + ASSERT(constLayer != nullptr); constLayer->setName("Matrix in multiplicative attention"); auto weights = constLayer->getOutput(0); - assert(weights != nullptr); + ASSERT(weights != nullptr); - auto mmLayer = network->addMatrixMultiply(*memoryStates, false, *weights, false); - assert(mmLayer != nullptr); + auto mmLayer = network->addMatrixMultiply(*memoryStates, MatrixOperation::kNONE, *weights, MatrixOperation::kNONE); + ASSERT(mmLayer != nullptr); mmLayer->setName("Attention Keys MM in multiplicative attention"); *attentionKeys = mmLayer->getOutput(0); - assert(*attentionKeys != nullptr); + ASSERT(*attentionKeys != nullptr); } int MultiplicativeAlignment::getSourceStatesSize() diff --git a/samples/opensource/sampleNMT/model/slpAttention.cpp b/samples/opensource/sampleNMT/model/slpAttention.cpp index 7da0f6c5..3ce17950 100644 --- a/samples/opensource/sampleNMT/model/slpAttention.cpp +++ b/samples/opensource/sampleNMT/model/slpAttention.cpp @@ -14,9 +14,8 @@ * limitations under the License. */ +#include "common.h" #include "slpAttention.h" - -#include #include namespace nmtSample @@ -25,9 +24,9 @@ SLPAttention::SLPAttention(ComponentWeights::ptr weights) : mWeights(weights) { // please refer to chpt_to_bin.py for the details on the format - assert(mWeights->mMetaData.size() >= 3); + ASSERT(mWeights->mMetaData.size() >= 3); mKernelWeights.type = static_cast(mWeights->mMetaData[0]); - assert(mKernelWeights.type == nvinfer1::DataType::kFLOAT); + ASSERT(mKernelWeights.type == nvinfer1::DataType::kFLOAT); mInputChannelCount = mWeights->mMetaData[1]; mOutputChannelCount = mWeights->mMetaData[2]; @@ -40,29 +39,29 @@ void SLPAttention::addToModel(nvinfer1::INetworkDefinition* network, nvinfer1::I { nvinfer1::ITensor* inputTensors[] = {inputFromDecoder, context}; auto concatLayer = network->addConcatenation(inputTensors, 2); - assert(concatLayer != nullptr); + ASSERT(concatLayer != nullptr); concatLayer->setName("Concatinate decoder output and context"); concatLayer->setAxis(1); auto concatinatedTensor = concatLayer->getOutput(0); - assert(concatinatedTensor != nullptr); + ASSERT(concatinatedTensor != nullptr); - nvinfer1::Dims weightDims{2, {mInputChannelCount, mOutputChannelCount}, - {nvinfer1::DimensionType::kCHANNEL, nvinfer1::DimensionType::kCHANNEL}}; + nvinfer1::Dims weightDims{2, {mInputChannelCount, mOutputChannelCount}}; auto constLayer = network->addConstant(weightDims, mKernelWeights); - assert(constLayer != nullptr); + ASSERT(constLayer != nullptr); constLayer->setName("Attention Matrix"); auto weights = constLayer->getOutput(0); - assert(weights != nullptr); + ASSERT(weights != nullptr); - auto mmLayer = network->addMatrixMultiply(*concatinatedTensor, false, *weights, false); - assert(mmLayer != nullptr); + auto mmLayer + = network->addMatrixMultiply(*concatinatedTensor, MatrixOperation::kNONE, *weights, MatrixOperation::kNONE); + ASSERT(mmLayer != nullptr); mmLayer->setName("Attention Matrix Multiply"); auto actLayer = network->addActivation(*mmLayer->getOutput(0), nvinfer1::ActivationType::kTANH); - assert(actLayer != nullptr); + ASSERT(actLayer != nullptr); *attentionOutput = actLayer->getOutput(0); - assert(*attentionOutput != nullptr); + ASSERT(*attentionOutput != nullptr); } int SLPAttention::getAttentionSize() diff --git a/samples/opensource/sampleNMT/model/slpEmbedder.cpp b/samples/opensource/sampleNMT/model/slpEmbedder.cpp index adb264f8..6729f050 100644 --- a/samples/opensource/sampleNMT/model/slpEmbedder.cpp +++ b/samples/opensource/sampleNMT/model/slpEmbedder.cpp @@ -17,7 +17,6 @@ #include "slpEmbedder.h" #include "common.h" -#include #include namespace nmtSample @@ -26,9 +25,9 @@ SLPEmbedder::SLPEmbedder(ComponentWeights::ptr weights) : mWeights(weights) { // please refer to chpt_to_bin.py for the details on the format - assert(mWeights->mMetaData.size() >= 3); + ASSERT(mWeights->mMetaData.size() >= 3); mKernelWeights.type = static_cast(mWeights->mMetaData[0]); - assert(mKernelWeights.type == nvinfer1::DataType::kFLOAT); + ASSERT(mKernelWeights.type == nvinfer1::DataType::kFLOAT); // Resize dimensions to be multiples of gPadMultiple for performance mNumInputs = samplesCommon::roundUp(mWeights->mMetaData[1], gPadMultiple); // matches projection output channels mNumOutputs = samplesCommon::roundUp(mWeights->mMetaData[2], gPadMultiple); // matches projection input channels @@ -41,19 +40,18 @@ SLPEmbedder::SLPEmbedder(ComponentWeights::ptr weights) void SLPEmbedder::addToModel( nvinfer1::INetworkDefinition* network, nvinfer1::ITensor* input, nvinfer1::ITensor** output) { - nvinfer1::Dims weightDims{ - 2, {mNumInputs, mNumOutputs}, {nvinfer1::DimensionType::kCHANNEL, nvinfer1::DimensionType::kCHANNEL}}; + nvinfer1::Dims weightDims{2, {mNumInputs, mNumOutputs}}; auto constLayer = network->addConstant(weightDims, mKernelWeights); - assert(constLayer != nullptr); + ASSERT(constLayer != nullptr); constLayer->setName("Embedding matrix"); auto weights = constLayer->getOutput(0); - assert(weights != nullptr); + ASSERT(weights != nullptr); auto gatherLayer = network->addGather(*weights, *input, 0); - assert(gatherLayer != nullptr); + ASSERT(gatherLayer != nullptr); gatherLayer->setName("Gather in embedding"); *output = gatherLayer->getOutput(0); - assert(*output != nullptr); + ASSERT(*output != nullptr); } int SLPEmbedder::getInputDimensionSize() diff --git a/samples/opensource/sampleNMT/model/slpProjection.cpp b/samples/opensource/sampleNMT/model/slpProjection.cpp index c55a3cb2..cb468518 100644 --- a/samples/opensource/sampleNMT/model/slpProjection.cpp +++ b/samples/opensource/sampleNMT/model/slpProjection.cpp @@ -16,8 +16,6 @@ #include "slpProjection.h" #include "common.h" - -#include #include namespace nmtSample @@ -26,9 +24,9 @@ SLPProjection::SLPProjection(ComponentWeights::ptr weights) : mWeights(weights) { // please refer to chpt_to_bin.py for the details on the format - assert(mWeights->mMetaData.size() >= 3); + ASSERT(mWeights->mMetaData.size() >= 3); mKernelWeights.type = static_cast(mWeights->mMetaData[0]); - assert(mKernelWeights.type == nvinfer1::DataType::kFLOAT); + ASSERT(mKernelWeights.type == nvinfer1::DataType::kFLOAT); // Resize dimensions to be multiples of gPadMultiple for performance mInputChannelCount = samplesCommon::roundUp(mWeights->mMetaData[1], gPadMultiple); // matches embedder outputs mOutputChannelCount = samplesCommon::roundUp(mWeights->mMetaData[2], gPadMultiple); // matches embedder inputs @@ -41,19 +39,18 @@ SLPProjection::SLPProjection(ComponentWeights::ptr weights) void SLPProjection::addToModel( nvinfer1::INetworkDefinition* network, nvinfer1::ITensor* input, nvinfer1::ITensor** outputLogits) { - nvinfer1::Dims weightDims{2, {mInputChannelCount, mOutputChannelCount}, - {nvinfer1::DimensionType::kCHANNEL, nvinfer1::DimensionType::kCHANNEL}}; + nvinfer1::Dims weightDims{2, {mInputChannelCount, mOutputChannelCount}}; auto constLayer = network->addConstant(weightDims, mKernelWeights); - assert(constLayer != nullptr); + ASSERT(constLayer != nullptr); constLayer->setName("Projection matrix"); auto weights = constLayer->getOutput(0); - assert(weights != nullptr); + ASSERT(weights != nullptr); - auto mmLayer = network->addMatrixMultiply(*input, false, *weights, false); - assert(mmLayer != nullptr); + auto mmLayer = network->addMatrixMultiply(*input, MatrixOperation::kNONE, *weights, MatrixOperation::kNONE); + ASSERT(mmLayer != nullptr); mmLayer->setName("Projection Matrix Multiply"); *outputLogits = mmLayer->getOutput(0); - assert(*outputLogits != nullptr); + ASSERT(*outputLogits != nullptr); } int SLPProjection::getOutputSize() diff --git a/samples/opensource/sampleNMT/model/softmaxLikelihood.cpp b/samples/opensource/sampleNMT/model/softmaxLikelihood.cpp index a16a0e2c..75996763 100644 --- a/samples/opensource/sampleNMT/model/softmaxLikelihood.cpp +++ b/samples/opensource/sampleNMT/model/softmaxLikelihood.cpp @@ -15,9 +15,7 @@ */ #include "softmaxLikelihood.h" - -#include - +#include "common.h" #include namespace nmtSample @@ -27,56 +25,56 @@ void SoftmaxLikelihood::addToModel(nvinfer1::INetworkDefinition* network, int be nvinfer1::ITensor** newRayOptionIndices, nvinfer1::ITensor** newVocabularyIndices) { auto softmaxLayer = network->addSoftMax(*inputLogits); - assert(softmaxLayer != nullptr); + ASSERT(softmaxLayer != nullptr); softmaxLayer->setName("Softmax in likelihood calculation"); softmaxLayer->setAxes(2); auto softmaxTensor = softmaxLayer->getOutput(0); - assert(softmaxTensor != nullptr); + ASSERT(softmaxTensor != nullptr); auto topKLayer = network->addTopK(*softmaxTensor, nvinfer1::TopKOperation::kMAX, beamWidth, 2); - assert(topKLayer != nullptr); + ASSERT(topKLayer != nullptr); topKLayer->setName("TopK 1st in likelihood calculation"); auto newLikelihoods = topKLayer->getOutput(0); - assert(newLikelihoods != nullptr); + ASSERT(newLikelihoods != nullptr); auto vocabularyIndices = topKLayer->getOutput(1); - assert(vocabularyIndices != nullptr); + ASSERT(vocabularyIndices != nullptr); auto eltWiseLayer = network->addElementWise(*newLikelihoods, *inputLikelihoods, nvinfer1::ElementWiseOperation::kPROD); - assert(eltWiseLayer != nullptr); + ASSERT(eltWiseLayer != nullptr); eltWiseLayer->setName("EltWise multiplication in likelihood calculation"); auto combinedLikelihoods = eltWiseLayer->getOutput(0); - assert(combinedLikelihoods != nullptr); + ASSERT(combinedLikelihoods != nullptr); auto shuffleLayer = network->addShuffle(*combinedLikelihoods); - assert(shuffleLayer != nullptr); + ASSERT(shuffleLayer != nullptr); shuffleLayer->setName("Reshape combined likelihoods"); - nvinfer1::Dims shuffleDims{1, {beamWidth * beamWidth}, {nvinfer1::DimensionType::kCHANNEL}}; + nvinfer1::Dims shuffleDims{1, {beamWidth * beamWidth}}; shuffleLayer->setReshapeDimensions(shuffleDims); auto reshapedCombinedLikelihoods = shuffleLayer->getOutput(0); - assert(reshapedCombinedLikelihoods != nullptr); + ASSERT(reshapedCombinedLikelihoods != nullptr); auto topKLayer2 = network->addTopK(*reshapedCombinedLikelihoods, nvinfer1::TopKOperation::kMAX, beamWidth, 1); - assert(topKLayer2 != nullptr); + ASSERT(topKLayer2 != nullptr); topKLayer2->setName("TopK 2nd in likelihood calculation"); *newCombinedLikelihoods = topKLayer2->getOutput(0); - assert(*newCombinedLikelihoods != nullptr); + ASSERT(*newCombinedLikelihoods != nullptr); *newRayOptionIndices = topKLayer2->getOutput(1); - assert(*newRayOptionIndices != nullptr); + ASSERT(*newRayOptionIndices != nullptr); auto shuffleLayer2 = network->addShuffle(*vocabularyIndices); - assert(shuffleLayer2 != nullptr); + ASSERT(shuffleLayer2 != nullptr); shuffleLayer2->setName("Reshape vocabulary indices"); - nvinfer1::Dims shuffleDims2{1, {beamWidth * beamWidth}, {nvinfer1::DimensionType::kCHANNEL}}; + nvinfer1::Dims shuffleDims2{1, {beamWidth * beamWidth}}; shuffleLayer2->setReshapeDimensions(shuffleDims2); auto reshapedVocabularyIndices = shuffleLayer2->getOutput(0); - assert(reshapedVocabularyIndices != nullptr); + ASSERT(reshapedVocabularyIndices != nullptr); auto gatherLayer = network->addGather(*reshapedVocabularyIndices, **newRayOptionIndices, 0); - assert(gatherLayer != nullptr); + ASSERT(gatherLayer != nullptr); gatherLayer->setName("Shuffle vocabulary indices"); *newVocabularyIndices = gatherLayer->getOutput(0); - assert(*newVocabularyIndices != nullptr); + ASSERT(*newVocabularyIndices != nullptr); } float SoftmaxLikelihood::SoftmaxLikelihoodCombinationOperator::combine( diff --git a/samples/opensource/sampleNMT/pinnedHostBuffer.h b/samples/opensource/sampleNMT/pinnedHostBuffer.h index 6341fc4a..4192f8e6 100644 --- a/samples/opensource/sampleNMT/pinnedHostBuffer.h +++ b/samples/opensource/sampleNMT/pinnedHostBuffer.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef SAMPLE_NMT_PINNED_HOST_BUFFER_ #define SAMPLE_NMT_PINNED_HOST_BUFFER_ diff --git a/samples/opensource/sampleNMT/sampleNMT.cpp b/samples/opensource/sampleNMT/sampleNMT.cpp index 08419f9e..449c9400 100644 --- a/samples/opensource/sampleNMT/sampleNMT.cpp +++ b/samples/opensource/sampleNMT/sampleNMT.cpp @@ -49,7 +49,6 @@ #include "model/beamSearchPolicy.h" #include "model/componentWeights.h" #include "model/contextNMT.h" -#include "model/debugUtil.h" #include "model/decoder.h" #include "model/embedder.h" #include "model/encoder.h" @@ -76,7 +75,7 @@ int gMaxOutputSequenceLength = -1; int gMaxInferenceSamples = -1; std::string gDataWriterStr = "bleu"; std::string gOutputTextFileName("translation_output.txt"); -int gMaxWorkspaceSize = 256_MiB; +int gMaxWorkspaceSize = 512_MiB; std::string gDataDirectory("data/samples/nmt/deen"); bool gEnableProfiling = false; bool gAggregateProfiling = false; @@ -116,8 +115,8 @@ nmtSample::DataReader::ptr getDataReader() { std::shared_ptr textInput(new std::ifstream(locateNMTFile(gInputTextFileName))); std::shared_ptr vocabInput(new std::ifstream(locateNMTFile(gInputVocabularyFileName))); - assert(textInput->good()); - assert(vocabInput->good()); + ASSERT(textInput->good()); + ASSERT(vocabInput->good()); auto vocabulary = std::make_shared(); *vocabInput >> *vocabulary; @@ -135,7 +134,7 @@ std::shared_ptr buildNMTComponentFromWeightsFile(const std::string& f { auto weights = std::make_shared(); std::ifstream input(locateNMTFile(filename), std::ios::binary); - assert(input.good()); + ASSERT(input.good()); input >> *weights; return std::make_shared(weights); @@ -197,14 +196,14 @@ nmtSample::DataWriter::ptr getDataWriter() if (gDataWriterStr == "bleu") { std::shared_ptr textInput(new std::ifstream(locateNMTFile(gReferenceOutputTextFileName))); - assert(textInput->good()); + ASSERT(textInput->good()); return std::make_shared(textInput, gOutputVocabulary); } else if (gDataWriterStr == "text") { std::remove(gOutputTextFileName.data()); std::shared_ptr textOutput(new std::ofstream(gOutputTextFileName)); - assert(textOutput->good() + ASSERT(textOutput->good() && "Please contact system administrator if you have no permission to write the file " "translation_output.txt"); return std::make_shared(textOutput, gOutputVocabulary); @@ -216,7 +215,7 @@ nmtSample::DataWriter::ptr getDataWriter() else { sample::gLogError << "Invalid data writer specified: " << gDataWriterStr << std::endl; - assert(0); + ASSERT(0); return nmtSample::DataWriter::ptr(); } } @@ -289,7 +288,7 @@ void printUsage() printf(" --verbose Output verbose-level messages by TensorRT\n"); printf(" --max_workspace_size= Maximum workspace size (default = %d)\n", gMaxWorkspaceSize); printf( - " --datadir= Path to the directory where data and weights are located (default = " + " --data_dir= Path to the directory where data and weights are located (default = " "%s)\n", gDataDirectory.c_str()); printf( @@ -336,7 +335,7 @@ bool parseNMTArgs(samplesCommon::Args& args, int argc, char* argv[]) continue; if (parseInt(argv[j], "max_workspace_size", gMaxWorkspaceSize)) continue; - if (parseString(argv[j], "datadir", gDataDirectory)) + if (parseString(argv[j], "data_dir", gDataDirectory)) continue; if (parseBool(argv[j], "profile", gEnableProfiling)) continue; @@ -366,7 +365,7 @@ nvinfer1::ICudaEngine* getEncoderEngine( nmtSample::Embedder::ptr inputEmbedder, nmtSample::Encoder::ptr encoder, nmtSample::Alignment::ptr alignment) { nvinfer1::IBuilder* encoderBuilder = nvinfer1::createInferBuilder(sample::gLogger.getTRTLogger()); - assert(encoderBuilder != nullptr); + ASSERT(encoderBuilder != nullptr); nvinfer1::IBuilderConfig* encoderConfig = encoderBuilder->createBuilderConfig(); encoderBuilder->setMaxBatchSize(gMaxBatchSize); encoderConfig->setMaxWorkspaceSize(gMaxWorkspaceSize); @@ -379,21 +378,21 @@ nvinfer1::ICudaEngine* getEncoderEngine( encoderConfig->setFlag(BuilderFlag::kINT8); } - nvinfer1::INetworkDefinition* encoderNetwork = encoderBuilder->createNetwork(); + nvinfer1::INetworkDefinition* encoderNetwork = encoderBuilder->createNetworkV2(0); // Define inputs for the encoder - nvinfer1::Dims inputDims{1, {gMaxInputSequenceLength}, {nvinfer1::DimensionType::kSEQUENCE}}; + nvinfer1::Dims inputDims{1, {gMaxInputSequenceLength}}; auto inputEncoderDataTensor = encoderNetwork->addInput("input_encoder_data", nvinfer1::DataType::kINT32, inputDims); - assert(inputEncoderDataTensor != nullptr); - nvinfer1::Dims inputSequenceLengthsDims{0, {}, {}}; + ASSERT(inputEncoderDataTensor != nullptr); + nvinfer1::Dims inputSequenceLengthsDims{0, {}}; auto actualInputSequenceLengthsTensor = encoderNetwork->addInput( "actual_input_sequence_lengths", nvinfer1::DataType::kINT32, inputSequenceLengthsDims); - assert(actualInputSequenceLengthsTensor != nullptr); - nvinfer1::Dims inputSequenceLengthsWithUnitIndexDims{1, {1}, {nvinfer1::DimensionType::kINDEX}}; + ASSERT(actualInputSequenceLengthsTensor != nullptr); + nvinfer1::Dims inputSequenceLengthsWithUnitIndexDims{1, {1}}; auto actualInputSequenceLengthsWithUnitIndexTensor = encoderNetwork->addInput("actual_input_sequence_lengths_with_index_dim", nvinfer1::DataType::kINT32, inputSequenceLengthsWithUnitIndexDims); - assert(actualInputSequenceLengthsWithUnitIndexTensor != nullptr); + ASSERT(actualInputSequenceLengthsWithUnitIndexTensor != nullptr); auto stateSizes = encoder->getStateSizes(); std::vector encoderInputStatesTensors(stateSizes.size()); @@ -403,16 +402,16 @@ nvinfer1::ICudaEngine* getEncoderEngine( ss << "input_encoder_states_" << i; encoderInputStatesTensors[i] = encoderNetwork->addInput( ss.str().c_str(), gFp16 ? nvinfer1::DataType::kHALF : nvinfer1::DataType::kFLOAT, stateSizes[i]); - assert(encoderInputStatesTensors[i] != nullptr); + ASSERT(encoderInputStatesTensors[i] != nullptr); } nvinfer1::ITensor* initializeDecoderIndicesTensor = nullptr; if (gInitializeDecoderFromEncoderHiddenStates) { - nvinfer1::Dims inputDims{1, {gBeamWidth}, {nvinfer1::DimensionType::kINDEX}}; + nvinfer1::Dims inputDims{1, {gBeamWidth}}; initializeDecoderIndicesTensor = encoderNetwork->addInput("initialize_decoder_indices", nvinfer1::DataType::kINT32, inputDims); - assert(initializeDecoderIndicesTensor != nullptr); + ASSERT(initializeDecoderIndicesTensor != nullptr); } nvinfer1::ITensor* inputEncoderEmbeddedTensor; @@ -441,10 +440,10 @@ nvinfer1::ICudaEngine* getEncoderEngine( { auto gatherLayer = encoderNetwork->addGather( *actualInputSequenceLengthsWithUnitIndexTensor, *initializeDecoderIndicesTensor, 0); - assert(gatherLayer != nullptr); + ASSERT(gatherLayer != nullptr); gatherLayer->setName("Replicate input sequence lengths for decoder"); auto actualInputSequenceLengthsReplicatedTensor = gatherLayer->getOutput(0); - assert(actualInputSequenceLengthsReplicatedTensor != nullptr); + ASSERT(actualInputSequenceLengthsReplicatedTensor != nullptr); actualInputSequenceLengthsReplicatedTensor->setName("actual_input_sequence_lengths_replicated"); encoderNetwork->markOutput(*actualInputSequenceLengthsReplicatedTensor); actualInputSequenceLengthsReplicatedTensor->setType(nvinfer1::DataType::kINT32); @@ -454,13 +453,13 @@ nvinfer1::ICudaEngine* getEncoderEngine( { for (int i = 0; i < static_cast(stateSizes.size()); ++i) { - assert(encoderOutputStatesTensors[i] != nullptr); + ASSERT(encoderOutputStatesTensors[i] != nullptr); // Insert index (Z=1) dimension into tensor nvinfer1::ITensor* encoderOutputStatesTensorWithUnitIndex; { auto shuffleLayer = encoderNetwork->addShuffle(*encoderOutputStatesTensors[i]); - assert(shuffleLayer != nullptr); + ASSERT(shuffleLayer != nullptr); { std::stringstream ss; ss << "Reshape encoder states for decoder initialization " << i; @@ -470,27 +469,25 @@ nvinfer1::ICudaEngine* getEncoderEngine( { shuffleDims.nbDims = stateSizes[i].nbDims + 1; shuffleDims.d[0] = 1; - shuffleDims.type[0] = nvinfer1::DimensionType::kINDEX; for (int j = 0; j < stateSizes[i].nbDims; ++j) { shuffleDims.d[j + 1] = stateSizes[i].d[j]; - shuffleDims.type[j + 1] = stateSizes[i].type[j]; } } shuffleLayer->setReshapeDimensions(shuffleDims); encoderOutputStatesTensorWithUnitIndex = shuffleLayer->getOutput(0); - assert(encoderOutputStatesTensorWithUnitIndex != nullptr); + ASSERT(encoderOutputStatesTensorWithUnitIndex != nullptr); } auto gatherLayer = encoderNetwork->addGather( *encoderOutputStatesTensorWithUnitIndex, *initializeDecoderIndicesTensor, 0); - assert(gatherLayer != nullptr); + ASSERT(gatherLayer != nullptr); { std::stringstream ss; ss << "Replicate encoder states for decoder initialization " << i; gatherLayer->setName(ss.str().c_str()); } auto inputDecoderHiddenStatesTensor = gatherLayer->getOutput(0); - assert(inputDecoderHiddenStatesTensor != nullptr); + ASSERT(inputDecoderHiddenStatesTensor != nullptr); std::stringstream ss; ss << "input_decoder_states_" << i; inputDecoderHiddenStatesTensor->setName(ss.str().c_str()); @@ -499,9 +496,15 @@ nvinfer1::ICudaEngine* getEncoderEngine( } } - samplesCommon::setDummyInt8Scales(encoderConfig, encoderNetwork); + samplesCommon::setDummyInt8DynamicRanges(encoderConfig, encoderNetwork); samplesCommon::enableDLA(encoderBuilder, encoderConfig, gUseDLACore); - auto res = encoderBuilder->buildEngineWithConfig(*encoderNetwork, *encoderConfig); + auto encoderPlan = encoderBuilder->buildSerializedNetwork(*encoderNetwork, *encoderConfig); + ASSERT(encoderPlan != nullptr); + auto runtime = createInferRuntime(sample::gLogger.getTRTLogger()); + ASSERT(runtime != nullptr); + auto res = runtime->deserializeCudaEngine(encoderPlan->data(), encoderPlan->size()); + runtime->destroy(); + encoderPlan->destroy(); encoderNetwork->destroy(); encoderBuilder->destroy(); encoderConfig->destroy(); @@ -513,7 +516,7 @@ nvinfer1::ICudaEngine* getGeneratorEngine(nmtSample::Embedder::ptr outputEmbedde nmtSample::Projection::ptr projection, nmtSample::Likelihood::ptr likelihood) { nvinfer1::IBuilder* generatorBuilder = nvinfer1::createInferBuilder(sample::gLogger.getTRTLogger()); - assert(generatorBuilder != nullptr); + ASSERT(generatorBuilder != nullptr); nvinfer1::IBuilderConfig* generatorConfig = generatorBuilder->createBuilderConfig(); generatorBuilder->setMaxBatchSize(gMaxBatchSize); generatorConfig->setMaxWorkspaceSize(gMaxWorkspaceSize); @@ -526,7 +529,7 @@ nvinfer1::ICudaEngine* getGeneratorEngine(nmtSample::Embedder::ptr outputEmbedde generatorConfig->setFlag(BuilderFlag::kINT8); } - nvinfer1::INetworkDefinition* generatorNetwork = generatorBuilder->createNetwork(); + nvinfer1::INetworkDefinition* generatorNetwork = generatorBuilder->createNetworkV2(0); // Define inputs for the generator auto stateSizes = decoder->getStateSizes(); @@ -539,63 +542,56 @@ nvinfer1::ICudaEngine* getGeneratorEngine(nmtSample::Embedder::ptr outputEmbedde { statesDims.nbDims = stateSizes[i].nbDims + 1; statesDims.d[0] = gBeamWidth; - statesDims.type[0] = nvinfer1::DimensionType::kINDEX; for (int j = 0; j < stateSizes[i].nbDims; ++j) { statesDims.d[j + 1] = stateSizes[i].d[j]; - statesDims.type[j + 1] = stateSizes[i].type[j]; } } decoderInputStatesTensors[i] = generatorNetwork->addInput( ss.str().c_str(), gFp16 ? nvinfer1::DataType::kHALF : nvinfer1::DataType::kFLOAT, statesDims); - assert(decoderInputStatesTensors[i] != nullptr); + ASSERT(decoderInputStatesTensors[i] != nullptr); } - nvinfer1::Dims inputDecoderDataDims{1, {gBeamWidth}, {nvinfer1::DimensionType::kINDEX}}; + nvinfer1::Dims inputDecoderDataDims{1, {gBeamWidth}}; auto inputDecoderDataTensor = generatorNetwork->addInput("input_decoder_data", nvinfer1::DataType::kINT32, inputDecoderDataDims); - assert(inputDecoderDataTensor != nullptr); - nvinfer1::Dims inputSequenceLengthsTeplicatedDims{ - 2, {gBeamWidth, 1}, {nvinfer1::DimensionType::kINDEX, nvinfer1::DimensionType::kCHANNEL}}; + ASSERT(inputDecoderDataTensor != nullptr); + nvinfer1::Dims inputSequenceLengthsTeplicatedDims{2, {gBeamWidth, 1}}; auto actualInputSequenceLengthsReplicatedTensor = generatorNetwork->addInput( "actual_input_sequence_lengths_replicated", nvinfer1::DataType::kINT32, inputSequenceLengthsTeplicatedDims); - assert(actualInputSequenceLengthsReplicatedTensor != nullptr); - nvinfer1::Dims memoryStatesDims{2, {gMaxInputSequenceLength, alignment->getSourceStatesSize()}, - {nvinfer1::DimensionType::kSEQUENCE, nvinfer1::DimensionType::kCHANNEL}}; + ASSERT(actualInputSequenceLengthsReplicatedTensor != nullptr); + nvinfer1::Dims memoryStatesDims{2, {gMaxInputSequenceLength, alignment->getSourceStatesSize()}}; auto memoryStatesTensor = generatorNetwork->addInput( "memory_states", gFp16 ? nvinfer1::DataType::kHALF : nvinfer1::DataType::kFLOAT, memoryStatesDims); - assert(memoryStatesTensor != nullptr); + ASSERT(memoryStatesTensor != nullptr); nvinfer1::ITensor* attentionKeysTensor = nullptr; if (alignment->getAttentionKeySize() > 0) { - nvinfer1::Dims attentionKeysDims{2, {gMaxInputSequenceLength, alignment->getAttentionKeySize()}, - {nvinfer1::DimensionType::kSEQUENCE, nvinfer1::DimensionType::kCHANNEL}}; + nvinfer1::Dims attentionKeysDims{2, {gMaxInputSequenceLength, alignment->getAttentionKeySize()}}; attentionKeysTensor = generatorNetwork->addInput( "attention_keys", gFp16 ? nvinfer1::DataType::kHALF : nvinfer1::DataType::kFLOAT, attentionKeysDims); - assert(attentionKeysTensor != nullptr); + ASSERT(attentionKeysTensor != nullptr); } nvinfer1::ITensor* inputAttentionTensor = nullptr; if (gFeedAttentionToInput) { - nvinfer1::Dims inputAttentionDims{2, {gBeamWidth, attention->getAttentionSize()}, - {nvinfer1::DimensionType::kINDEX, nvinfer1::DimensionType::kCHANNEL}}; + nvinfer1::Dims inputAttentionDims{2, {gBeamWidth, attention->getAttentionSize()}}; inputAttentionTensor = generatorNetwork->addInput( "input_attention", gFp16 ? nvinfer1::DataType::kHALF : nvinfer1::DataType::kFLOAT, inputAttentionDims); - assert(inputAttentionTensor != nullptr); + ASSERT(inputAttentionTensor != nullptr); } - nvinfer1::Dims inputLikelihoodsDims{ - 2, {gBeamWidth, 1}, {nvinfer1::DimensionType::kINDEX, nvinfer1::DimensionType::kCHANNEL}}; + nvinfer1::Dims inputLikelihoodsDims{2, {gBeamWidth, 1}}; auto inputLikelihoodsTensor = generatorNetwork->addInput("input_likelihoods", nvinfer1::DataType::kFLOAT, inputLikelihoodsDims); - assert(inputLikelihoodsTensor != nullptr); - nvinfer1::Dims inputLikelihoodsReplicateIndicesDims{1, {gBeamWidth}, {nvinfer1::DimensionType::kCHANNEL}}; + ASSERT(inputLikelihoodsTensor != nullptr); + nvinfer1::Dims inputLikelihoodsReplicateIndicesDims{1, {gBeamWidth}}; auto inputLikelihoodsReplicateIndicesTensor = generatorNetwork->addInput( "replicate_likelihoods_indices", nvinfer1::DataType::kINT32, inputLikelihoodsReplicateIndicesDims); - assert(inputLikelihoodsReplicateIndicesTensor != nullptr); + ASSERT(inputLikelihoodsReplicateIndicesTensor != nullptr); // Add output embedder nvinfer1::ITensor* inputDecoderEmbeddedTensor; outputEmbedder->addToModel(generatorNetwork, inputDecoderDataTensor, &inputDecoderEmbeddedTensor); - assert(inputDecoderEmbeddedTensor != nullptr); + ASSERT(inputDecoderEmbeddedTensor != nullptr); // Add concatination of previous attention vector and embedded input for the decoder nvinfer1::ITensor* inputDecoderEmbeddedConcatinatedWithAttentionTensor{nullptr}; @@ -603,11 +599,11 @@ nvinfer1::ICudaEngine* getGeneratorEngine(nmtSample::Embedder::ptr outputEmbedde { nvinfer1::ITensor* inputTensors[] = {inputDecoderEmbeddedTensor, inputAttentionTensor}; auto concatLayer = generatorNetwork->addConcatenation(inputTensors, 2); - assert(concatLayer != nullptr); + ASSERT(concatLayer != nullptr); concatLayer->setName("Concatenate embedded input and attention"); concatLayer->setAxis(1); inputDecoderEmbeddedConcatinatedWithAttentionTensor = concatLayer->getOutput(0); - assert(inputDecoderEmbeddedConcatinatedWithAttentionTensor != nullptr); + ASSERT(inputDecoderEmbeddedConcatinatedWithAttentionTensor != nullptr); } // Add decoder (single timestep) @@ -652,10 +648,10 @@ nvinfer1::ICudaEngine* getGeneratorEngine(nmtSample::Embedder::ptr outputEmbedde // Replicate input likelihoods across all TopK options auto gatherLayer = generatorNetwork->addGather(*inputLikelihoodsTensor, *inputLikelihoodsReplicateIndicesTensor, 1); - assert(gatherLayer != nullptr); + ASSERT(gatherLayer != nullptr); gatherLayer->setName("Replicate beam likelihoods"); auto inputLikelihoodsReplicatedTensor = gatherLayer->getOutput(0); - assert(inputLikelihoodsReplicatedTensor != nullptr); + ASSERT(inputLikelihoodsReplicatedTensor != nullptr); // Add per-ray top-k options generation nvinfer1::ITensor* outputCombinedLikelihoodsTensor; @@ -672,9 +668,15 @@ nvinfer1::ICudaEngine* getGeneratorEngine(nmtSample::Embedder::ptr outputEmbedde generatorNetwork->markOutput(*outputVocabularyIndicesTensor); outputVocabularyIndicesTensor->setType(nvinfer1::DataType::kINT32); - samplesCommon::setDummyInt8Scales(generatorConfig, generatorNetwork); + samplesCommon::setDummyInt8DynamicRanges(generatorConfig, generatorNetwork); samplesCommon::enableDLA(generatorBuilder, generatorConfig, gUseDLACore); - auto res = generatorBuilder->buildEngineWithConfig(*generatorNetwork, *generatorConfig); + auto generatorPlan = generatorBuilder->buildSerializedNetwork(*generatorNetwork, *generatorConfig); + ASSERT(generatorPlan != nullptr); + auto runtime = createInferRuntime(sample::gLogger.getTRTLogger()); + ASSERT(runtime != nullptr); + auto res = runtime->deserializeCudaEngine(generatorPlan->data(), generatorPlan->size()); + runtime->destroy(); + generatorPlan->destroy(); generatorNetwork->destroy(); generatorBuilder->destroy(); generatorConfig->destroy(); @@ -685,7 +687,7 @@ nvinfer1::ICudaEngine* getGeneratorShuffleEngine( const std::vector& decoderStateSizes, int attentionSize) { nvinfer1::IBuilder* shuffleBuilder = nvinfer1::createInferBuilder(sample::gLogger.getTRTLogger()); - assert(shuffleBuilder != nullptr); + ASSERT(shuffleBuilder != nullptr); nvinfer1::IBuilderConfig* shuffleConfig = shuffleBuilder->createBuilderConfig(); shuffleBuilder->setMaxBatchSize(gMaxBatchSize); shuffleConfig->setMaxWorkspaceSize(gMaxWorkspaceSize); @@ -698,12 +700,12 @@ nvinfer1::ICudaEngine* getGeneratorShuffleEngine( shuffleConfig->setFlag(BuilderFlag::kINT8); } - nvinfer1::INetworkDefinition* shuffleNetwork = shuffleBuilder->createNetwork(); + nvinfer1::INetworkDefinition* shuffleNetwork = shuffleBuilder->createNetworkV2(0); - nvinfer1::Dims sourceRayIndicesDims{1, {gBeamWidth}, {nvinfer1::DimensionType::kINDEX}}; + nvinfer1::Dims sourceRayIndicesDims{1, {gBeamWidth}}; auto sourceRayIndicesTensor = shuffleNetwork->addInput("source_ray_indices", nvinfer1::DataType::kINT32, sourceRayIndicesDims); - assert(sourceRayIndicesTensor != nullptr); + ASSERT(sourceRayIndicesTensor != nullptr); std::vector previousOutputDecoderStatesTensors(decoderStateSizes.size()); for (int i = 0; i < static_cast(decoderStateSizes.size()); ++i) @@ -714,40 +716,37 @@ nvinfer1::ICudaEngine* getGeneratorShuffleEngine( { statesDims.nbDims = decoderStateSizes[i].nbDims + 1; statesDims.d[0] = gBeamWidth; - statesDims.type[0] = nvinfer1::DimensionType::kINDEX; for (int j = 0; j < decoderStateSizes[i].nbDims; ++j) { statesDims.d[j + 1] = decoderStateSizes[i].d[j]; - statesDims.type[j + 1] = decoderStateSizes[i].type[j]; } } previousOutputDecoderStatesTensors[i] = shuffleNetwork->addInput( ss.str().c_str(), gFp16 ? nvinfer1::DataType::kHALF : nvinfer1::DataType::kFLOAT, statesDims); - assert(previousOutputDecoderStatesTensors[i] != nullptr); + ASSERT(previousOutputDecoderStatesTensors[i] != nullptr); } nvinfer1::ITensor* previousOutputAttentionTensor = nullptr; if (gFeedAttentionToInput) { - nvinfer1::Dims previousOutputAttentionDims{ - 2, {gBeamWidth, attentionSize}, {nvinfer1::DimensionType::kINDEX, nvinfer1::DimensionType::kCHANNEL}}; + nvinfer1::Dims previousOutputAttentionDims{2, {gBeamWidth, attentionSize}}; previousOutputAttentionTensor = shuffleNetwork->addInput("previous_output_attention", gFp16 ? nvinfer1::DataType::kHALF : nvinfer1::DataType::kFLOAT, previousOutputAttentionDims); - assert(previousOutputAttentionTensor != nullptr); + ASSERT(previousOutputAttentionTensor != nullptr); } for (int i = 0; i < static_cast(decoderStateSizes.size()); ++i) { auto gatherLayer = shuffleNetwork->addGather(*previousOutputDecoderStatesTensors[i], *sourceRayIndicesTensor, 0); - assert(gatherLayer != nullptr); + ASSERT(gatherLayer != nullptr); { std::stringstream ss; ss << "Shuffle decoder states " << i; gatherLayer->setName(ss.str().c_str()); } auto inputDecoderHiddenStatesTensor = gatherLayer->getOutput(0); - assert(inputDecoderHiddenStatesTensor != nullptr); + ASSERT(inputDecoderHiddenStatesTensor != nullptr); std::stringstream ss; ss << "input_decoder_states_" << i; inputDecoderHiddenStatesTensor->setName(ss.str().c_str()); @@ -758,18 +757,24 @@ nvinfer1::ICudaEngine* getGeneratorShuffleEngine( if (gFeedAttentionToInput) { auto gatherLayer = shuffleNetwork->addGather(*previousOutputAttentionTensor, *sourceRayIndicesTensor, 0); - assert(gatherLayer != nullptr); + ASSERT(gatherLayer != nullptr); gatherLayer->setName("Shuffle attention"); auto inputAttentionTensor = gatherLayer->getOutput(0); - assert(inputAttentionTensor != nullptr); + ASSERT(inputAttentionTensor != nullptr); inputAttentionTensor->setName("input_attention"); shuffleNetwork->markOutput(*inputAttentionTensor); inputAttentionTensor->setType(gFp16 ? nvinfer1::DataType::kHALF : nvinfer1::DataType::kFLOAT); } - samplesCommon::setDummyInt8Scales(shuffleConfig, shuffleNetwork); + samplesCommon::setDummyInt8DynamicRanges(shuffleConfig, shuffleNetwork); samplesCommon::enableDLA(shuffleBuilder, shuffleConfig, gUseDLACore); - auto res = shuffleBuilder->buildEngineWithConfig(*shuffleNetwork, *shuffleConfig); + auto shufflePlan = shuffleBuilder->buildSerializedNetwork(*shuffleNetwork, *shuffleConfig); + ASSERT(shufflePlan != nullptr); + auto runtime = createInferRuntime(sample::gLogger.getTRTLogger()); + ASSERT(runtime != nullptr); + auto res = runtime->deserializeCudaEngine(shufflePlan->data(), shufflePlan->size()); + runtime->destroy(); + shufflePlan->destroy(); shuffleNetwork->destroy(); shuffleBuilder->destroy(); shuffleConfig->destroy(); @@ -787,7 +792,7 @@ void processBindings( for (auto& a : bindingMap) { auto bindIdx = engine->getBindingIndex(a.first.c_str()); - assert(bindIdx >= 0 && bindIdx < engine->getNbBindings()); + ASSERT(bindIdx >= 0 && bindIdx < engine->getNbBindings()); bindings[bindIdx] = a.second; } } @@ -864,15 +869,15 @@ int main(int argc, char** argv) std::vector stateSizes = decoder->getStateSizes(); // A number of consistency checks between components - assert(alignment->getSourceStatesSize() == encoder->getMemoryStatesSize()); + ASSERT(alignment->getSourceStatesSize() == encoder->getMemoryStatesSize()); if (gInitializeDecoderFromEncoderHiddenStates) { std::vector encoderStateSizes = encoder->getStateSizes(); - assert(stateSizes.size() == encoderStateSizes.size()); + ASSERT(stateSizes.size() == encoderStateSizes.size()); for (int i = 0; i < static_cast(stateSizes.size()); ++i) - assert(nmtSample::getVolume(stateSizes[i]) == nmtSample::getVolume(encoderStateSizes[i])); + ASSERT(nmtSample::getVolume(stateSizes[i]) == nmtSample::getVolume(encoderStateSizes[i])); } - assert(projection->getOutputSize() == outputEmbedder->getInputDimensionSize()); + ASSERT(projection->getOutputSize() == outputEmbedder->getInputDimensionSize()); auto inputOriginalHostBuffer = std::make_shared>(gMaxBatchSize * gMaxInputSequenceLength); diff --git a/samples/opensource/sampleNMT/trtUtil.cpp b/samples/opensource/sampleNMT/trtUtil.cpp index e6906640..423a0865 100644 --- a/samples/opensource/sampleNMT/trtUtil.cpp +++ b/samples/opensource/sampleNMT/trtUtil.cpp @@ -14,9 +14,8 @@ * limitations under the License. */ +#include "common.h" #include "trtUtil.h" - -#include #include #include @@ -28,10 +27,10 @@ int inferTypeToBytes(nvinfer1::DataType t) { case nvinfer1::DataType::kFLOAT: return sizeof(float); break; case nvinfer1::DataType::kHALF: return sizeof(int16_t); break; - default: assert(0); break; + default: ASSERT(0); break; } return 0; -}; +} int getVolume(nvinfer1::Dims dims) { diff --git a/samples/opensource/sampleNMT/trtUtil.h b/samples/opensource/sampleNMT/trtUtil.h index c6193b7b..67207c64 100644 --- a/samples/opensource/sampleNMT/trtUtil.h +++ b/samples/opensource/sampleNMT/trtUtil.h @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef SAMPLE_NMT_TRT_UTIL_ #define SAMPLE_NMT_TRT_UTIL_ diff --git a/samples/opensource/sampleOnnxMNIST/README.md b/samples/opensource/sampleOnnxMNIST/README.md index e5eea16e..ddd27a7e 100644 --- a/samples/opensource/sampleOnnxMNIST/README.md +++ b/samples/opensource/sampleOnnxMNIST/README.md @@ -8,7 +8,6 @@ * [Building the engine](#building-the-engine) * [Running inference](#running-inference) * [TensorRT API layers and ops](#tensorrt-api-layers-and-ops) -- [Preparing sample data](#preparing-sample-data) - [Running the sample](#running-the-sample) * [Sample `--help` options](#sample-help-options) - [Additional resources](#additional-resources) @@ -94,26 +93,21 @@ The Scale layer implements a per-tensor, per-channel, or per-element affine tran [Shuffle layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#shuffle-layer) The Shuffle layer implements a reshape and transpose operator for tensors. -## Preparing sample data - -1. Download the sample data from [TensorRT release tarball](https://developer.nvidia.com/nvidia-tensorrt-download#), if not already mounted under `/usr/src/tensorrt/data` (NVIDIA NGC containers) and set it to `$TRT_DATADIR`. - ```bash - export TRT_DATADIR=/usr/src/tensorrt/data - ``` ## Running the sample -1. Compile the sample by following build instructions in [TensorRT README](https://github.com/NVIDIA/TensorRT/). - -2. Run the sample to build and run the MNIST engine from the ONNX model. - ```bash - sample_onnx_mnist [-h or --help] [-d or --datadir=] [--useDLACore=] [--int8 or --fp16] +1. Compile this sample by running `make` in the `/samples/sampleOnnxMNIST` directory. The binary named `sample_onnx_mnist` will be created in the `/bin` directory. + ``` + cd /samples/sampleOnnxMNIST + make ``` - For example: - ```bash - sample_onnx_mnist --datadir $TRT_DATADIR/mnist - ``` + Where `` is where you installed TensorRT. + +2. Run the sample to build and run the MNIST engine from the ONNX model. + ``` + ./sample_onnx_mnist [-h or --help] [-d or --datadir=] [--useDLACore=] [--int8 or --fp16] + ``` 3. Verify that the sample ran successfully. If the sample runs successfully you should see output similar to the following: ``` diff --git a/samples/opensource/sampleOnnxMNIST/sampleOnnxMNIST.cpp b/samples/opensource/sampleOnnxMNIST/sampleOnnxMNIST.cpp index ec6ca1a7..96263dec 100644 --- a/samples/opensource/sampleOnnxMNIST/sampleOnnxMNIST.cpp +++ b/samples/opensource/sampleOnnxMNIST/sampleOnnxMNIST.cpp @@ -37,6 +37,8 @@ #include #include +using samplesCommon::SampleUniquePtr; + const std::string gSampleName = "TensorRT.sample_onnx_mnist"; //! \brief The SampleOnnxMNIST class implements the ONNX MNIST sample @@ -45,9 +47,6 @@ const std::string gSampleName = "TensorRT.sample_onnx_mnist"; //! class SampleOnnxMNIST { - template - using SampleUniquePtr = std::unique_ptr; - public: SampleOnnxMNIST(const samplesCommon::OnnxSampleParams& params) : mParams(params) @@ -134,20 +133,40 @@ bool SampleOnnxMNIST::build() return false; } + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { + return false; + } + config->setProfileStream(*profileStream); + + SampleUniquePtr plan{builder->buildSerializedNetwork(*network, *config)}; + if (!plan) + { + return false; + } + + SampleUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + if (!runtime) + { + return false; + } + mEngine = std::shared_ptr( - builder->buildEngineWithConfig(*network, *config), samplesCommon::InferDeleter()); + runtime->deserializeCudaEngine(plan->data(), plan->size()), samplesCommon::InferDeleter()); if (!mEngine) { return false; } - assert(network->getNbInputs() == 1); + ASSERT(network->getNbInputs() == 1); mInputDims = network->getInput(0)->getDimensions(); - assert(mInputDims.nbDims == 4); + ASSERT(mInputDims.nbDims == 4); - assert(network->getNbOutputs() == 1); + ASSERT(network->getNbOutputs() == 1); mOutputDims = network->getOutput(0)->getDimensions(); - assert(mOutputDims.nbDims == 2); + ASSERT(mOutputDims.nbDims == 2); return true; } @@ -179,7 +198,7 @@ bool SampleOnnxMNIST::constructNetwork(SampleUniquePtr& buil if (mParams.int8) { config->setFlag(BuilderFlag::kINT8); - samplesCommon::setAllTensorScales(network.get(), 127.0f, 127.0f); + samplesCommon::setAllDynamicRanges(network.get(), 127.0f, 127.0f); } samplesCommon::enableDLA(builder.get(), config.get(), mParams.dlaCore); @@ -205,7 +224,7 @@ bool SampleOnnxMNIST::infer() } // Read the input data into the managed buffers - assert(mParams.inputTensorNames.size() == 1); + ASSERT(mParams.inputTensorNames.size() == 1); if (!processInput(buffers)) { return false; diff --git a/samples/opensource/sampleOnnxMnistCoordConvAC/sampleOnnxMnistCoordConvAC.cpp b/samples/opensource/sampleOnnxMnistCoordConvAC/sampleOnnxMnistCoordConvAC.cpp index 2de3befd..547d025e 100644 --- a/samples/opensource/sampleOnnxMnistCoordConvAC/sampleOnnxMnistCoordConvAC.cpp +++ b/samples/opensource/sampleOnnxMnistCoordConvAC/sampleOnnxMnistCoordConvAC.cpp @@ -37,6 +37,8 @@ #include #include +using samplesCommon::SampleUniquePtr; + const std::string gSampleName = "TensorRT.sample_onnx_mnist_coord_conv_ac"; // Normalization constants from Pytorch transform.Normalize(). @@ -51,9 +53,6 @@ const float PYTORCH_NORMALIZE_STD = 0.3081; //! class SampleOnnxMnistCoordConvAC { - template - using SampleUniquePtr = std::unique_ptr; - public: SampleOnnxMnistCoordConvAC(const samplesCommon::OnnxSampleParams& params) : mParams(params) @@ -142,6 +141,14 @@ bool SampleOnnxMnistCoordConvAC::build() return false; } + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { + return false; + } + config->setProfileStream(*profileStream); + mEngine = std::shared_ptr( builder->buildEngineWithConfig(*network, *config), samplesCommon::InferDeleter()); if (!mEngine) @@ -187,7 +194,7 @@ bool SampleOnnxMnistCoordConvAC::constructNetwork(SampleUniquePtrsetFlag(BuilderFlag::kINT8); - samplesCommon::setAllTensorScales(network.get(), 127.0f, 127.0f); + samplesCommon::setAllDynamicRanges(network.get(), 127.0f, 127.0f); } samplesCommon::enableDLA(builder.get(), config.get(), mParams.dlaCore); diff --git a/samples/opensource/samplePlugin/README.md b/samples/opensource/samplePlugin/README.md deleted file mode 100644 index dc0c4693..00000000 --- a/samples/opensource/samplePlugin/README.md +++ /dev/null @@ -1,345 +0,0 @@ -# Adding A Custom Layer To Your Network In TensorRT - -**Table Of Contents** - -- [Description](#description) -- [How does this sample work?](#how-does-this-sample-work) - * [Defining the network](#defining-the-network) - * [Enabling custom layers in NvCaffeParser](#enabling-custom-layers-in-nvcaffeparser) - * [Building the engine](#building-the-engine) - * [Serializing and deserializing](#serializing-and-deserializing) - * [Resource management and execution](#resource-management-and-execution) - * [TensorRT API layers and ops](#tensorrt-api-layers-and-ops) -- [Preparing sample data](#preparing-sample-data) -- [Running the sample](#running-the-sample) - * [Sample `--help` options](#sample-help-options) -- [Additional resources](#additional-resources) -- [License](#license) -- [Changelog](#changelog) -- [Known issues](#known-issues) - -## Description - -This sample, samplePlugin, defines a custom layer that supports multiple data formats and demonstrates how to serialize/deserialize plugin layers. This sample also demonstrates how to use a fully connected plugin (`FCPlugin`) as a custom layer and the integration with NvCaffeParser. - -## How does this sample work? - -This sample implements the MNIST model (`data/samples/mnist/mnist.prototxt`) with the difference that the custom layer implements the Caffe InnerProduct layer using gemm routines (Matrix Multiplication) in cuBLAS and tensor addition in cuDNN (bias offset). Normally, the Caffe InnerProduct layer can be implemented in TensorRT using the IFullyConnected layer. However, in this sample, we use `FCPlugin` for this layer as an example of how to use plugins. The sample demonstrates plugin usage through the `IPluginExt` interface and uses the `nvcaffeparser1::IPluginFactoryExt` to add the plugin object to the network. - -Specifically, this sample: -- [Defines the network](#defining-the-network) -- [Enables custom layers](#enabling-custom-layers-in-nvcaffeparser) -- [Builds the engine](#building-the-engine) -- [Serialize and deserialize](#serializing-and-deserializing) -- [Manages resources and executes the engine](#resource-management-and-execution) - -### Defining the network - -The `FCPlugin` redefines the InnerProduct layer, which has a single output. Accordingly, `getNbOutputs` returns `1` and `getOutputDimensions` includes validation checks and returns the dimensions of the output: - -```c++ -Dims getOutputDimensions(int index, const Dims* inputDims, - int nbInputDims) override -{ - assert(index == 0 && nbInputDims == 1 && - inputDims[0].nbDims == 3); - assert(mNbInputChannels == inputDims[0].d[0] * - inputDims[0].d[1] * - inputDims[0].d[2]); - return DimsCHW(mNbOutputChannels, 1, 1); -} -``` - -### Enabling custom layers in NvCaffeParser - -The model is imported using the Caffe parser (see [Importing A Caffe Model Using The C++ Parser API](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#import_caffe_c) and [Using Custom Layers When Importing A Model From a Framework](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#using_custom_layer)). To use the `FCPlugin` implementation for the InnerProduct layer, a plugin factory is defined which recognizes the name of the InnerProduct layer (inner product `ip2` in Caffe). - -```c++ -bool isPlugin(const char* name) override -{ return !strcmp(name, "ip2"); } -``` - -The factory can then instantiate `FCPlugin` objects as directed by the parser. The `createPlugin` method receives the layer name, and a set of weights extracted from the Caffe model file, which are then passed to the plugin constructor. Since the lifetime of the weights and that of the newly created plugin are decoupled, the plugin makes a copy of the weights in the constructor. - -```c++ -virtual nvinfer1::IPlugin* createPlugin(const char* layerName, const nvinfer1::Weights* weights, int nbWeights) override -{ - ... - mPlugin = - std::unique_ptr(new FCPlugin(weights,nbWeights)); - - return mPlugin.get(); -} -``` - -### Building the engine - -`FCPlugin` does not need any scratch space, therefore, for building the engine, the most important methods deal with the formats supported and the configuration. `FCPlugin` supports two formats: NCHW in both single and half precision as defined in the `supportsFormat` method. - -```c++ -bool supportsFormat(DataType type, PluginFormat format) const override -{ - return (type == DataType::kFLOAT || type == DataType::kHALF) && - format == PluginFormat::kNCHW; -} -``` - -Supported configurations are selected in the building phase. The builder selects a configuration with the networks `configureWithFormat()` method, to give it a chance to select an algorithm based on its inputs. In this example, the inputs are checked to ensure they are in a supported format, and the selected format is recorded in a member variable. No other information needs to be stored in this simple case; in more complex cases, you may need to do so or even choose an ad-hoc algorithm for the given configuration. - -```c++ -void configureWithFormat(..., DataType type, PluginFormat format, ...) override -{ - assert((type == DataType::kFLOAT || type == DataType::kHALF) && - format == PluginFormat::kNCHW); - mDataType = type; - -} -``` - -The configuration takes place at build time, therefore, any information or state determined here that is required at runtime should be stored as a member variable of the plugin, and serialized and deserialized. - -### Serializing and deserializing - -Fully compliant plugins support serialization and deserialization, as described in [Serializing A Model In C++](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#serial_model_c). In the example, `FCPlugin` stores the number of channels and weights, the format selected, and the actual weights. The size of these variables makes up for the size of the serialized image; the size is returned by `getSerializationSize`: - -```c++ -virtual size_t getSerializationSize() override -{ - return sizeof(mNbInputChannels) + sizeof(mNbOutputChannels) + - sizeof(mBiasWeights.count) + sizeof(mDataType) + - (mKernelWeights.count + mBiasWeights.count) * - type2size(mDataType); -} -``` - -Eventually, when the engine is serialized, these variables are serialized, the weights converted is needed, and written on a buffer: - -```c++ -virtual void serialize(void* buffer) override -{ - char* d = static_cast(buffer), *a = d; - write(d, mNbInputChannels); - ... - convertAndCopyToBuffer(d, mKernelWeights); - convertAndCopyToBuffer(d, mBiasWeights); - assert(d == a + getSerializationSize()); -} -``` - -Then, when the engine is deployed, it is deserialized. As the runtime scans the serialized image, when a plugin image is encountered, it create a new plugin instance via the factory. The plugin object created during deserialization (shown below using new) is destroyed when the engine is destroyed by calling `FCPlugin::destroy()`. - -```c++ -IPlugin* createPlugin(...) override -{ - ... - return new FCPlugin(serialData, serialLength); -} -``` - -In the same order as in the serialization, the variables are read and their values restored. In addition, at this point the weights have been converted to selected format and can be stored directly on the device. - -```c++ -FCPlugin(const void* data, size_t length) -{ - const char* d = static_cast(data), *a = d; - read(d, mNbInputChannels); - ... - deserializeToDevice(d, mDeviceKernel, - mKernelWeights.count*type2size(mDataType)); - deserializeToDevice(d, mDeviceBias, - mBiasWeights.count*type2size(mDataType)); - assert(d == a + length); -} -``` - -### Resource management and execution - -Before a custom layer is executed, the plugin is initialized. This is where resources are held for the lifetime of the plugin and can be acquired and initialized. In this example, weights are kept in CPU memory at first, so that during the build phase, for each configuration tested, weights can be converted to the desired format and then copied to the device in the initialization of the plugin. The method `initialize` creates the required cuBLAS and cuDNN handles, sets up tensor descriptors, allocates device memory, and copies the weights to device memory. Conversely, terminate destroys the handles and frees the memory allocated on the device. - -```c++ -int initialize() override -{ - CHECK(cudnnCreate(&mCudnn)); - CHECK(cublasCreate(&mCublas)); - ... - if (mKernelWeights.values != nullptr) - convertAndCopyToDevice(mDeviceKernel, mKernelWeights); - ... -} -``` - -The core of the plugin is `enqueue`, which is used to execute the custom layer at runtime. The `call` parameters include the actual batch size, inputs, and outputs. The handles for cuBLAS and cuDNN operations are placed on the given stream; then, according to the data type and format configured, the plugin executes in single or half precision. - -**Note:** The two handles are part of the plugin object, therefore, the same engine cannot be executed concurrently on multiple streams. In order to enable multiple streams of execution, plugins must be re-entrant and handle stream-specific data accordingly. - -```c++ -virtual int enqueue(int batchSize, const void*const * inputs, void** outputs, ...) override -{ - ... - cublasSetStream(mCublas, stream); - cudnnSetStream(mCudnn, stream); - if (mDataType == DataType::kFLOAT) - {...} - else - { - CHECK(cublasHgemm(mCublas, CUBLAS_OP_T, CUBLAS_OP_N, - mNbOutputChannels, batchSize, - mNbInputChannels, &oneh, - mDeviceKernel), mNbInputChannels, - inputs[0], mNbInputChannels, &zeroh, - outputs[0], mNbOutputChannels)); - } - if (mBiasWeights.count) - { - cudnnDataType_t cudnnDT = mDataType == DataType::kFLOAT ? - CUDNN_DATA_FLOAT : CUDNN_DATA_HALF; - ... - } - return 0; -} -``` - -The plugin object created in the sample is cloned by each of the network, builder, and engine by calling the `FCPlugin::clone()` method. The `clone()` method calls the plugin constructor and can also clone plugin parameters, if necessary. - -```c++ -IPluginExt* clone() -{ - return new FCPlugin(&mKernelWeights, mNbWeights, mNbOutputChannels); -} -``` - -The cloned plugin objects are deleted when the network, builder, or engine are destroyed. This is done by invoking the `FCPlugin::destroy()` method. -`void destroy() { delete this; }` - - -### TensorRT API layers and ops - -In this sample, the following layers are used. For more information about these layers, see the [TensorRT Developer Guide: Layers](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#layers) documentation. - -[Activation layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#activation-layer) -The Activation layer implements element-wise activation functions. Specifically, this sample uses the Activation layer with the type `kRELU`. - -[Convolution layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#convolution-layer) -The Convolution layer computes a 2D (channel, height, and width) convolution, with or without bias. - -[FullyConnected layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#fullyconnected-layer) -The FullyConnected layer implements a matrix-vector product, with or without bias. - -[Pooling layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#pooling-layer) -The Pooling layer implements pooling within a channel. Supported pooling types are `maximum`, `average` and `maximum-average blend`. - -[Scale layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#scale-layer) -The Scale layer implements a per-tensor, per-channel, or per-element affine transformation and/or exponentiation by constant values. - -[SoftMax layer](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#softmax-layer) -The SoftMax layer applies the SoftMax function on the input tensor along an input dimension specified by the user. - -## Preparing sample data - -1. Download the sample data from [TensorRT release tarball](https://developer.nvidia.com/nvidia-tensorrt-download#), if not already mounted under `/usr/src/tensorrt/data` (NVIDIA NGC containers) and set it to `$TRT_DATADIR`. - ```bash - export TRT_DATADIR=/usr/src/tensorrt/data - pushd $TRT_DATADIR/mnist - pip install Pillow - python3 download_pgms.py - popd - ``` - -## Running the sample - -1. Compile the sample by following build instructions in [TensorRT README](https://github.com/NVIDIA/TensorRT/). - -2. Run the sample to perform inference on the digit: - ```bash - sample_plugin --datadir= - ``` - - For example: - ```bash - sample_plugin --datadir $TRT_DATADIR/mnist - ``` - -3. Verify that the sample ran successfully. If the sample runs successfully you should see output similar to the following: - ``` - &&&& RUNNING TensorRT.sample_plugin # ./build/x86_64-linux/sample_plugin - [I] [TRT] Detected 1 input and 1 output network tensors. - [I] Input: - @@@@@@@@@@@@@@@@@@@@@@@@@@@@ - @@@@@@@@@@@@@@@@@@@@@@@@@@@@ - @@@@@@@@@@@@@@@@@@@@@@@@@@@@ - @@@@@@@@@@@@@@@@@@@@@@@@@@@@ - @@@@@@@@@@@@@@@@@@@@@@@@@@@@ - @@@@@@@@@@@@@@@@@@@@@@@@@@@@ - @@@@@@@@@@@@@@%.-@@@@@@@@@@@ - @@@@@@@@@@@*- %@@@@@@@@@@ - @@@@@@@@@@= .-. *@@@@@@@@@@ - @@@@@@@@@= +@@@ *@@@@@@@@@@ - @@@@@@@@* =@@@@ %@@@@@@@@@@ - @@@@@@@@..@@@@% @@@@@@@@@@@ - @@@@@@@# *@@@@- @@@@@@@@@@@ - @@@@@@@: @@@@% @@@@@@@@@@@ - @@@@@@@: @@@@- @@@@@@@@@@@ - @@@@@@@: =+*= +: *@@@@@@@@@@ - @@@@@@@*. +@: *@@@@@@@@@@ - @@@@@@@@%#**#@@: *@@@@@@@@@@ - @@@@@@@@@@@@@@@: -@@@@@@@@@@ - @@@@@@@@@@@@@@@+ :@@@@@@@@@@ - @@@@@@@@@@@@@@@* @@@@@@@@@@ - @@@@@@@@@@@@@@@@ %@@@@@@@@@ - @@@@@@@@@@@@@@@@ #@@@@@@@@@ - @@@@@@@@@@@@@@@@: +@@@@@@@@@ - @@@@@@@@@@@@@@@@- +@@@@@@@@@ - @@@@@@@@@@@@@@@@*:%@@@@@@@@@ - @@@@@@@@@@@@@@@@@@@@@@@@@@@@ - @@@@@@@@@@@@@@@@@@@@@@@@@@@@ - - [I] Output: - 0: - 1: - 2: - 3: - 4: - 5: - 6: - 7: - 8: - 9: ********** - - &&&& PASSED TensorRT.sample_plugin # ./build/x86_64-linux/sample_plugin - ``` - - This output shows that the sample ran successfully; `PASSED`. - - -### Sample `--help` options - -To see the full list of available options and their descriptions, use the `-h` or `--help` command line option. - - -# Additional resources - -The following resources provide a deeper understanding about samplePlugin: - -**Models** -- [Training LeNet on MNIST with Caffe](http://caffe.berkeleyvision.org/gathered/examples/mnist.html) -- [lenet.prototxt](https://github.com/BVLC/caffe/blob/master/examples/mnist/lenet.prototxt) - -**Documentation** -- [Introduction To NVIDIA’s TensorRT Samples](https://docs.nvidia.com/deeplearning/sdk/tensorrt-sample-support-guide/index.html#samples) -- [Working With TensorRT Using The C++ API](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#c_topics) -- [NVIDIA’s TensorRT Documentation Library](https://docs.nvidia.com/deeplearning/sdk/tensorrt-archived/index.html) - -# 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 - -February 2019 -This is the first release of this `README.md` file. - - -# Known issues - -There are no known issues in this sample. diff --git a/samples/opensource/samplePlugin/fcPlugin.h b/samples/opensource/samplePlugin/fcPlugin.h deleted file mode 100644 index 86a50fe2..00000000 --- a/samples/opensource/samplePlugin/fcPlugin.h +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "NvInfer.h" -#include "common.h" -#include "fp16.h" - -class FCPlugin : public nvinfer1::IPluginExt -{ -public: - FCPlugin(const nvinfer1::Weights* weights, int nbWeights, int nbOutputChannels) - : mNbOutputChannels(nbOutputChannels) - { - assert(nbWeights == 2); - - mKernelWeights = weights[0]; - assert(mKernelWeights.type == nvinfer1::DataType::kFLOAT || mKernelWeights.type == nvinfer1::DataType::kHALF); - - mBiasWeights = weights[1]; - assert(mBiasWeights.count == 0 || mBiasWeights.count == nbOutputChannels); - assert(mBiasWeights.type == nvinfer1::DataType::kFLOAT || mBiasWeights.type == nvinfer1::DataType::kHALF); - - mKernelWeights.values = malloc(mKernelWeights.count * type2size(mKernelWeights.type)); - std::memcpy(const_cast(mKernelWeights.values), weights[0].values, - mKernelWeights.count * type2size(mKernelWeights.type)); - mBiasWeights.values = malloc(mBiasWeights.count * type2size(mBiasWeights.type)); - std::memcpy(const_cast(mBiasWeights.values), weights[1].values, - mBiasWeights.count * type2size(mBiasWeights.type)); - - mNbInputChannels = int(weights[0].count / nbOutputChannels); - } - - // create the plugin at runtime from a byte stream - FCPlugin(const void* data, size_t length) - { - const char *d = static_cast(data), *a = d; - read(d, mNbInputChannels); - read(d, mNbOutputChannels); - - mKernelWeights.count = mNbInputChannels * mNbOutputChannels; - mKernelWeights.values = nullptr; - - read(d, mBiasWeights.count); - mBiasWeights.values = nullptr; - - read(d, mDataType); - - deserializeToDevice(d, mDeviceKernel, mKernelWeights.count * type2size(mDataType)); - deserializeToDevice(d, mDeviceBias, mBiasWeights.count * type2size(mDataType)); - assert(d == a + length); - } - - ~FCPlugin() - { - if (mKernelWeights.values) - { - free(const_cast(mKernelWeights.values)); - mKernelWeights.values = nullptr; - } - if (mBiasWeights.values) - { - free(const_cast(mBiasWeights.values)); - mBiasWeights.values = nullptr; - } - } - - int getNbOutputs() const override - { - return 1; - } - - nvinfer1::Dims getOutputDimensions(int index, const nvinfer1::Dims* inputs, int nbInputDims) override - { - assert(index == 0 && nbInputDims == 1 && inputs[0].nbDims == 3); - assert(mNbInputChannels == inputs[0].d[0] * inputs[0].d[1] * inputs[0].d[2]); - return nvinfer1::Dims3(mNbOutputChannels, 1, 1); - } - - bool supportsFormat(nvinfer1::DataType type, nvinfer1::PluginFormat format) const override - { - int device; - CHECK(cudaGetDevice(&device)); - cudaDeviceProp props{}; - cudaGetDeviceProperties(&props, device); - int smVersion = props.major << 8 | props.minor; - // Half precision is supported after SM60 - return (type == nvinfer1::DataType::kFLOAT || (type == nvinfer1::DataType::kHALF && smVersion >= 0x600)) - && format == nvinfer1::PluginFormat::kNCHW; - } - - void configureWithFormat(const nvinfer1::Dims* inputDims, int nbInputs, const nvinfer1::Dims* outputDims, - int nbOutputs, nvinfer1::DataType type, nvinfer1::PluginFormat format, int maxBatchSize) override - { - assert((type == nvinfer1::DataType::kFLOAT || type == nvinfer1::DataType::kHALF) - && format == nvinfer1::PluginFormat::kNCHW); - mDataType = type; - } - - int initialize() override - { - CHECK(cudnnCreate(&mCudnn)); // initialize cudnn and cublas - CHECK(cublasCreate(&mCublas)); - CHECK( - cudnnCreateTensorDescriptor(&mSrcDescriptor)); // create cudnn tensor descriptors we need for bias addition - CHECK(cudnnCreateTensorDescriptor(&mDstDescriptor)); - if (mKernelWeights.values) - { - convertAndCopyToDevice(mDeviceKernel, mKernelWeights); - } - if (mBiasWeights.values) - { - convertAndCopyToDevice(mDeviceBias, mBiasWeights); - } - - return 0; - } - - virtual void terminate() override - { - CHECK(cudnnDestroyTensorDescriptor(mSrcDescriptor)); - CHECK(cudnnDestroyTensorDescriptor(mDstDescriptor)); - CHECK(cublasDestroy(mCublas)); - CHECK(cudnnDestroy(mCudnn)); - if (mDeviceKernel) - { - cudaFree(mDeviceKernel); - mDeviceKernel = nullptr; - } - if (mDeviceBias) - { - cudaFree(mDeviceBias); - mDeviceBias = nullptr; - } - } - - virtual size_t getWorkspaceSize(int maxBatchSize) const override - { - return 0; - } - - virtual int enqueue( - int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) override - { - float onef{1.0f}, zerof{0.0f}; - __half oneh = fp16::__float2half(1.0f), zeroh = fp16::__float2half(0.0f); - - cublasSetStream(mCublas, stream); - cudnnSetStream(mCudnn, stream); - - if (mDataType == nvinfer1::DataType::kFLOAT) - { - CHECK(cublasSgemm(mCublas, CUBLAS_OP_T, CUBLAS_OP_N, mNbOutputChannels, batchSize, mNbInputChannels, &onef, - reinterpret_cast(mDeviceKernel), mNbInputChannels, - reinterpret_cast(inputs[0]), mNbInputChannels, &zerof, - reinterpret_cast(outputs[0]), mNbOutputChannels)); - } - else - { - CHECK(cublasHgemm(mCublas, CUBLAS_OP_T, CUBLAS_OP_N, mNbOutputChannels, batchSize, mNbInputChannels, &oneh, - reinterpret_cast(mDeviceKernel), mNbInputChannels, - reinterpret_cast(inputs[0]), mNbInputChannels, &zeroh, - reinterpret_cast<__half*>(outputs[0]), mNbOutputChannels)); - } - if (mBiasWeights.count) - { - cudnnDataType_t cudnnDT = mDataType == nvinfer1::DataType::kFLOAT ? CUDNN_DATA_FLOAT : CUDNN_DATA_HALF; - CHECK(cudnnSetTensor4dDescriptor(mSrcDescriptor, CUDNN_TENSOR_NCHW, cudnnDT, 1, mNbOutputChannels, 1, 1)); - CHECK(cudnnSetTensor4dDescriptor( - mDstDescriptor, CUDNN_TENSOR_NCHW, cudnnDT, batchSize, mNbOutputChannels, 1, 1)); - CHECK(cudnnAddTensor(mCudnn, &onef, mSrcDescriptor, mDeviceBias, &onef, mDstDescriptor, outputs[0])); - } - - return 0; - } - - virtual size_t getSerializationSize() override - { - return sizeof(mNbInputChannels) + sizeof(mNbOutputChannels) + sizeof(mBiasWeights.count) + sizeof(mDataType) - + (mKernelWeights.count + mBiasWeights.count) * type2size(mDataType); - } - - virtual void serialize(void* buffer) override - { - char *d = static_cast(buffer), *a = d; - - write(d, mNbInputChannels); - write(d, mNbOutputChannels); - write(d, mBiasWeights.count); - write(d, mDataType); - convertAndCopyToBuffer(d, mKernelWeights); - convertAndCopyToBuffer(d, mBiasWeights); - assert(d == a + getSerializationSize()); - } - -private: - size_t type2size(nvinfer1::DataType type) - { - return type == nvinfer1::DataType::kFLOAT ? sizeof(float) : sizeof(__half); - } - - template - void write(char*& buffer, const T& val) - { - *reinterpret_cast(buffer) = val; - buffer += sizeof(T); - } - - template - void read(const char*& buffer, T& val) - { - val = *reinterpret_cast(buffer); - buffer += sizeof(T); - } - - void* copyToDevice(const void* data, size_t count) - { - void* deviceData; - CHECK(cudaMalloc(&deviceData, count)); - CHECK(cudaMemcpy(deviceData, data, count, cudaMemcpyHostToDevice)); - return deviceData; - } - - void convertAndCopyToDevice(void*& deviceWeights, const nvinfer1::Weights& weights) - { - if (weights.type != mDataType) // Weights are converted in host memory first, if the type does not match - { - size_t size = weights.count * (mDataType == nvinfer1::DataType::kFLOAT ? sizeof(float) : sizeof(__half)); - void* buffer = malloc(size); - for (int64_t v = 0; v < weights.count; ++v) - { - if (mDataType == nvinfer1::DataType::kFLOAT) - { - static_cast(buffer)[v] = fp16::__half2float(static_cast(weights.values)[v]); - } - else - { - static_cast<__half*>(buffer)[v] = fp16::__float2half(static_cast(weights.values)[v]); - } - } - deviceWeights = copyToDevice(buffer, size); - free(buffer); - } - else - { - deviceWeights = copyToDevice(weights.values, weights.count * type2size(mDataType)); - } - } - - void convertAndCopyToBuffer(char*& buffer, const nvinfer1::Weights& weights) - { - if (weights.type != mDataType) - { - for (int64_t v = 0; v < weights.count; ++v) - { - if (mDataType == nvinfer1::DataType::kFLOAT) - { - reinterpret_cast(buffer)[v] - = fp16::__half2float(static_cast(weights.values)[v]); - } - else - { - reinterpret_cast<__half*>(buffer)[v] - = fp16::__float2half(static_cast(weights.values)[v]); - } - } - } - else - { - std::memcpy(buffer, weights.values, weights.count * type2size(mDataType)); - } - buffer += weights.count * type2size(mDataType); - } - - void deserializeToDevice(const char*& hostBuffer, void*& deviceWeights, size_t size) - { - deviceWeights = copyToDevice(hostBuffer, size); - hostBuffer += size; - } - - int mNbOutputChannels, mNbInputChannels; - nvinfer1::Weights mKernelWeights, mBiasWeights; - - nvinfer1::DataType mDataType{nvinfer1::DataType::kFLOAT}; - void* mDeviceKernel{nullptr}; - void* mDeviceBias{nullptr}; - - cudnnHandle_t mCudnn; - cublasHandle_t mCublas; - cudnnTensorDescriptor_t mSrcDescriptor, mDstDescriptor; -}; - -// integration for serialization -class PluginFactory : public nvinfer1::IPluginFactory, public nvcaffeparser1::IPluginFactoryExt -{ -public: - // caffe parser plugin implementation - bool isPlugin(const char* name) override - { - return isPluginExt(name); - } - - bool isPluginExt(const char* name) override - { - return !strcmp(name, "ip2"); - } - - virtual IPlugin* createPlugin(const char* layerName, const nvinfer1::Weights* weights, int nbWeights) override - { - try - { - // there's no way to pass parameters through from the model definition, so we have to define it here - // explicitly - static const int NB_OUTPUT_CHANNELS = 10; - assert(isPlugin(layerName) && nbWeights == 2); - assert(mPlugin.get() == nullptr); - mPlugin = std::unique_ptr(new FCPlugin(weights, nbWeights, NB_OUTPUT_CHANNELS)); - return mPlugin.get(); - } - catch (std::exception& e) - { - sample::gLogError << e.what() << std::endl; - } - - return nullptr; - } - - // deserialization plugin implementation - nvinfer1::IPlugin* createPlugin(const char* layerName, const void* serialData, size_t serialLength) override - { - try - { - assert(isPlugin(layerName)); - // IPlugin resource will not be released when engine destroy. - // Use this unique ptr in factory to release the data. - mPlugin = std::unique_ptr(new FCPlugin(serialData, serialLength)); - return mPlugin.get(); - } - catch (std::exception& e) - { - sample::gLogError << e.what() << std::endl; - } - - return nullptr; - } - - // User application destroys plugin when it is safe to do so. - // Should be done after consumers of plugin (like ICudaEngine) are destroyed. - void destroyPlugin() - { - mPlugin.reset(); - } - - std::unique_ptr mPlugin{nullptr}; -}; diff --git a/samples/opensource/samplePlugin/fp16.h b/samples/opensource/samplePlugin/fp16.h deleted file mode 100644 index a644e2a9..00000000 --- a/samples/opensource/samplePlugin/fp16.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. - * - * 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. - */ - -#ifndef _TRT_FP16_H_ -#define _TRT_FP16_H_ - -#include - -namespace fp16 -{ -// Code added before equivalent code was available via cuda. -// This code needs to be removed when we ship for cuda-9.2. -template -T bitwise_cast(U u) -{ - return *reinterpret_cast(&u); -} - -__half __float2half(float f) -{ - uint32_t x = bitwise_cast(f); - uint32_t u = (x & 0x7fffffff); - - // Get rid of +NaN/-NaN case first. - if (u > 0x7f800000) - return bitwise_cast<__half, uint16_t>(uint16_t(0x7fff)); - - uint16_t sign = ((x >> 16) & 0x8000); - - // Get rid of +Inf/-Inf, +0/-0. - if (u > 0x477fefff) - return bitwise_cast<__half, uint16_t>(sign | uint16_t(0x7c00)); - - if (u < 0x33000001) - return bitwise_cast<__half, uint16_t>(sign | uint16_t(0x0000)); - - uint32_t exponent = ((u >> 23) & 0xff); - uint32_t mantissa = (u & 0x7fffff); - - uint32_t shift; - if (exponent > 0x70) - { - shift = 13; - exponent -= 0x70; - } - else - { - shift = 0x7e - exponent; - exponent = 0; - mantissa |= 0x800000; - } - - uint32_t lsb = (1 << shift); - uint32_t lsb_s1 = (lsb >> 1); - uint32_t lsb_m1 = (lsb - 1); - - // Round to nearest even. - uint32_t remainder = (mantissa & lsb_m1); - mantissa >>= shift; - if ((remainder > lsb_s1) || ((remainder == lsb_s1) && (mantissa & 0x1))) - { - ++mantissa; - if (!(mantissa & 0x3ff)) - { - ++exponent; - mantissa = 0; - } - } - - return bitwise_cast<__half, uint16_t>(sign | uint16_t(exponent << 10) | uint16_t(mantissa)); -} - -float __half2float(__half h) -{ - uint16_t x = bitwise_cast(h); - uint32_t sign = ((x >> 15) & 1); - uint32_t exponent = ((x >> 10) & 0x1f); - uint32_t mantissa = (static_cast(x & 0x3ff) << 13); - - if (exponent == 0x1f) - { /* NaN or Inf */ - if (mantissa != 0) - { // NaN - sign = 0; - mantissa = 0x7fffff; - } - else // Inf - mantissa = 0; - exponent = 0xff; - } - else if (!exponent) - { /* Denorm or Zero */ - if (mantissa) - { - unsigned int msb; - exponent = 0x71; - do - { - msb = (mantissa & 0x400000); - mantissa <<= 1; /* normalize */ - --exponent; - } while (!msb); - mantissa &= 0x7fffff; /* 1.mantissa is implicit */ - } - } - else - exponent += 0x70; - return bitwise_cast((sign << 31) | (exponent << 23) | mantissa); -} - -}; // namespace fp16 - -#endif // _TRT_FP16_H_ diff --git a/samples/opensource/samplePlugin/samplePlugin.cpp b/samples/opensource/samplePlugin/samplePlugin.cpp deleted file mode 100644 index 2c5af47c..00000000 --- a/samples/opensource/samplePlugin/samplePlugin.cpp +++ /dev/null @@ -1,454 +0,0 @@ -/* - * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. - * - * 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. - */ - -//! \file samplePlugin.cpp -//! \brief This file contains the implementation of the samplePlugin. -//! -//! It builds a TensorRT engine by importing a trained MNIST Caffe model, and replaces the final -//! Fully Connected (FC) layer with a custom plugin layer. It uses the engine to run inference on an input image of a -//! digit. It can be run with the following command line: Command: ./sample_plugin [-h or --help] [-d=/path/to/data/dir -//! or --datadir=/path/to/data/dir] - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "NvCaffeParser.h" -#include "NvInfer.h" -#include "argsParser.h" -#include "buffers.h" -#include "common.h" -#include "fcPlugin.h" -#include "logger.h" - -const std::string gSampleName = "TensorRT.sample_plugin"; - -//! -//! \brief The SamplePlugin class implements samplePlugin. -//! -//! \details It creates the network using a trained Caffe MNIST classification model, and replaces the -//! final FC layer with a custom plugin layer. -//! -class SamplePlugin -{ - template - using SampleUniquePtr = std::unique_ptr; - -public: - SamplePlugin(const samplesCommon::CaffeSampleParams& params) - : mParams(params) - { - } - - ~SamplePlugin() - { - // Release the engine first before the plugin released. - mEngine.reset(); - } - - //! - //! \brief Builds the network engine - //! - bool build(); - - //! - //! \brief Runs the TensorRT inference engine for this sample - //! - bool infer(); - - //! - //! \brief Used to clean up any state created in the sample class - //! - bool teardown(); - -private: - //! - //! \brief Uses a Caffe parser to create the MNIST Network and marks the - //! output layers. - //! - void constructNetwork(SampleUniquePtr& builder, - SampleUniquePtr& parser, SampleUniquePtr& network); - - //! - //! \brief Reads the input and mean data, preprocesses, and stores the result in a managed buffer - //! - bool processInput( - const samplesCommon::BufferManager& buffers, const std::string& inputTensorName, int inputFileIdx) const; - - //! - //! \brief Verifies that the output is correct and prints it - //! - bool verifyOutput( - const samplesCommon::BufferManager& buffers, const std::string& outputTensorName, int groundTruthDigit) const; - - std::shared_ptr mEngine{nullptr}; //!< The TensorRT engine used to run the network - - samplesCommon::CaffeSampleParams mParams; //!< The parameters for the sample. - - SampleUniquePtr - mMeanBlob; //!< The mean blob, which need to keep around until build time - - nvinfer1::Dims mInputDims; //!< The dimensions of the input to the network. - - PluginFactory runtimePluginFactory; -}; - -//! -//! \brief Creates the network, configures the builder and creates the network engine -//! -//! \details This function creates the MNIST network by parsing the caffe model and builds -//! the engine with a custom FC plugin layer -//! -//! \return Returns true if the engine was created successfully and false otherwise -//! -bool SamplePlugin::build() -{ - auto builder = SampleUniquePtr(nvinfer1::createInferBuilder(sample::gLogger.getTRTLogger())); - if (!builder) - { - return false; - } - - auto network = SampleUniquePtr(builder->createNetwork()); - if (!network) - { - return false; - } - - auto config = SampleUniquePtr(builder->createBuilderConfig()); - if (!config) - { - return false; - } - - auto parser = SampleUniquePtr(nvcaffeparser1::createCaffeParser()); - if (!parser) - { - return false; - } - - // The PluginFactory object contains the methods to construct the FC plugin layer - // that are needed to create the engine - PluginFactory parserPluginFactory; - parser->setPluginFactoryExt(&parserPluginFactory); - constructNetwork(builder, parser, network); - - builder->setMaxBatchSize(mParams.batchSize); - config->setMaxWorkspaceSize(1_MiB); - if (mParams.fp16) - { - config->setFlag(BuilderFlag::kFP16); - } - if (mParams.int8) - { - config->setFlag(BuilderFlag::kINT8); - } - samplesCommon::setAllTensorScales(network.get(), 127.0f, 127.0f); - - samplesCommon::enableDLA(builder.get(), config.get(), mParams.dlaCore); - - // For illustrative purposes, we will use the builder to create a CUDA engine, - // serialize it to mModelStream object (which can be written to a file), then - // deserialize mModelStream with a IRuntime object to recreate the original engine. - // Note for this sample we could have simply used the original engine produced by builder->buildEngineWithConfig() - auto builtEngine = SampleUniquePtr(builder->buildEngineWithConfig(*network, *config)); - auto modelStream = SampleUniquePtr(builtEngine->serialize()); - assert(modelStream != nullptr); - - auto runtime = SampleUniquePtr(nvinfer1::createInferRuntime(sample::gLogger.getTRTLogger())); - if (mParams.dlaCore >= 0) - { - runtime->setDLACore(mParams.dlaCore); - } - - mEngine = std::shared_ptr( - runtime->deserializeCudaEngine(modelStream->data(), modelStream->size(), &runtimePluginFactory), - samplesCommon::InferDeleter()); - - sample::gLogInfo << "Done preparing engine..." << std::endl; - - assert(network->getNbInputs() == 1); - mInputDims = network->getInput(0)->getDimensions(); - assert(mInputDims.nbDims == 3); - - return true; -} - -//! -//! \brief Uses a caffe parser to create the MNIST Network and marks the -//! output layers -//! -//! \param network Pointer to the network that will be populated with the MNIST network -//! -//! \param builder Pointer to the engine builder -//! -void SamplePlugin::constructNetwork(SampleUniquePtr& builder, - SampleUniquePtr& parser, SampleUniquePtr& network) -{ - auto dType = builder->platformHasFastFp16() ? nvinfer1::DataType::kHALF : nvinfer1::DataType::kFLOAT; - - const nvcaffeparser1::IBlobNameToTensor* blobNameToTensor - = parser->parse(mParams.prototxtFileName.c_str(), mParams.weightsFileName.c_str(), *network, dType); - - for (auto& s : mParams.outputTensorNames) - { - network->markOutput(*blobNameToTensor->find(s.c_str())); - } - - // Parse mean blob for preprocessing input later - mMeanBlob - = SampleUniquePtr(parser->parseBinaryProto(mParams.meanFileName.c_str())); - sample::gLogInfo << "Done constructing network..." << std::endl; -} - -//! -//! \brief Runs the TensorRT inference engine for this sample -//! -//! \details This function is the main execution function of the sample. It allocates -//! the buffer, sets inputs, executes the engine, and verifies the output. -//! -bool SamplePlugin::infer() -{ - // Create RAII buffer manager object - samplesCommon::BufferManager buffers(mEngine, mParams.batchSize); - - auto context = SampleUniquePtr(mEngine->createExecutionContext()); - if (!context) - { - return false; - } - - // Pick a random digit to try to infer - srand(time(NULL)); - const int digit = rand() % 10; - - // Read the input data into the managed buffers - // There should be just 1 input tensor - assert(mParams.inputTensorNames.size() == 1); - if (!processInput(buffers, mParams.inputTensorNames[0], digit)) - { - return false; - } - // Create CUDA stream for the execution of this inference. - cudaStream_t stream; - CHECK(cudaStreamCreate(&stream)); - - // Asynchronously copy data from host input buffers to device input buffers - buffers.copyInputToDeviceAsync(stream); - - // Asynchronously enqueue the inference work - if (!context->enqueue(mParams.batchSize, buffers.getDeviceBindings().data(), stream, nullptr)) - { - return false; - } - // Asynchronously copy data from device output buffers to host output buffers - buffers.copyOutputToHostAsync(stream); - - // Wait for the work in the stream to complete - cudaStreamSynchronize(stream); - - // Release stream - cudaStreamDestroy(stream); - - // Check and print the output of the inference - // There should be just one output tensor - assert(mParams.outputTensorNames.size() == 1); - bool outputCorrect = verifyOutput(buffers, mParams.outputTensorNames[0], digit); - - // The output correctness is not used to determine the test result. - if (!outputCorrect && mParams.dlaCore != -1) - { - sample::gLogInfo << "Warning: infer result is not correct. It maybe caused by dummy scales in INT8 mode." - << std::endl; - } - - return true; -} - -//! -//! \brief Reads the input and mean data, preprocesses, and stores the result in a managed buffer -//! -bool SamplePlugin::processInput( - const samplesCommon::BufferManager& buffers, const std::string& inputTensorName, int inputFileIdx) const -{ - const int inputH = mInputDims.d[1]; - const int inputW = mInputDims.d[2]; - - // Read a random digit file - srand(unsigned(time(nullptr))); - std::vector fileData(inputH * inputW); - readPGMFile(locateFile(std::to_string(inputFileIdx) + ".pgm", mParams.dataDirs), fileData.data(), inputH, inputW); - - // Print ASCII representation of digit - sample::gLogInfo << "Input:\n"; - for (int i = 0; i < inputH * inputW; i++) - { - sample::gLogInfo << (" .:-=+*#%@"[fileData[i] / 26]) << (((i + 1) % inputW) ? "" : "\n"); - } - sample::gLogInfo << std::endl; - - float* hostInputBuffer = static_cast(buffers.getHostBuffer(inputTensorName)); - const float* meanData = reinterpret_cast(mMeanBlob->getData()); - - for (int i = 0; i < inputH * inputW; i++) - { - hostInputBuffer[i] = static_cast(fileData[i]) - meanData[i]; - } - - return true; -} - -//! -//! \brief Verifies that the output is correct and prints it -//! -bool SamplePlugin::verifyOutput( - const samplesCommon::BufferManager& buffers, const std::string& outputTensorName, int groundTruthDigit) const -{ - const float* prob = static_cast(buffers.getHostBuffer(outputTensorName)); - - // Print histogram of the output distribution - sample::gLogInfo << "Output:\n"; - float val{0.0f}; - int idx{0}; - const int kDIGITS = 10; - - for (int i = 0; i < kDIGITS; i++) - { - if (val < prob[i]) - { - val = prob[i]; - idx = i; - } - - sample::gLogInfo << i << ": " << std::string(int(std::floor(prob[i] * 10 + 0.5f)), '*') << "\n"; - } - sample::gLogInfo << std::endl; - - return (idx == groundTruthDigit && val > 0.9f); -} - -//! -//! \brief Used to clean up any state created in the sample class -//! -bool SamplePlugin::teardown() -{ - //! Clean up the libprotobuf files as the parsing is complete - //! \note It is not safe to use any other part of the protocol buffers library after - //! ShutdownProtobufLibrary() has been called. - nvcaffeparser1::shutdownProtobufLibrary(); - return true; -} - -//! -//! \brief Initializes members of the params struct using the command line args -//! -samplesCommon::CaffeSampleParams initializeSampleParams(const samplesCommon::Args& args) -{ - samplesCommon::CaffeSampleParams params; - if (args.dataDirs.empty()) //!< Use default directories if user hasn't provided directory paths - { - params.dataDirs.push_back("data/mnist/"); - params.dataDirs.push_back("data/samples/mnist/"); - } - else //!< Use the data directory provided by the user - { - params.dataDirs = args.dataDirs; - } - - params.prototxtFileName = locateFile("mnist.prototxt", params.dataDirs); - params.weightsFileName = locateFile("mnist.caffemodel", params.dataDirs); - params.meanFileName = locateFile("mnist_mean.binaryproto", params.dataDirs); - params.inputTensorNames.push_back("data"); - params.batchSize = 1; - params.outputTensorNames.push_back("prob"); - params.dlaCore = args.useDLACore; - params.int8 = args.runInInt8; - params.fp16 = args.runInFp16; - - return params; -} - -//! -//! \brief Prints the help information for running this sample -//! -void printHelpInfo() -{ - std::cout - << "Usage: ./sample_plugin [-h or --help] [-d or --datadir=] [--useDLACore=]\n"; - std::cout << "--help Display help information\n"; - std::cout << "--datadir Specify path to a data directory, overriding the default. This option can be used " - "multiple times to add multiple directories. If no data directories are given, the default is to use " - "(data/samples/mnist/, data/mnist/)" - << std::endl; - std::cout << "--useDLACore=N Specify a DLA engine for layers that support DLA. Value can range from 0 to n-1, " - "where n is the number of DLA engines on the platform." - << std::endl; - std::cout << "--int8 Run in Int8 mode." << std::endl; - std::cout << "--fp16 Run in FP16 mode." << std::endl; -} - -int main(int argc, char** argv) -{ - samplesCommon::Args args; - bool argsOK = samplesCommon::parseArgs(args, argc, argv); - if (!argsOK) - { - sample::gLogError << "Invalid arguments" << std::endl; - printHelpInfo(); - return EXIT_FAILURE; - } - if (args.help) - { - printHelpInfo(); - return EXIT_SUCCESS; - } - - auto sampleTest = sample::gLogger.defineTest(gSampleName, argc, argv); - - sample::gLogger.reportTestStart(sampleTest); - - samplesCommon::CaffeSampleParams params = initializeSampleParams(args); - - SamplePlugin sample(params); - sample::gLogInfo << "Building and running a GPU inference engine for MNIST" << std::endl; - - if (!sample.build()) - { - return sample::gLogger.reportFail(sampleTest); - } - - if (!sample.infer()) - { - return sample::gLogger.reportFail(sampleTest); - } - - if (!sample.teardown()) - { - return sample::gLogger.reportFail(sampleTest); - } - - return sample::gLogger.reportPass(sampleTest); -} diff --git a/samples/opensource/sampleReformatFreeIO/sampleReformatFreeIO.cpp b/samples/opensource/sampleReformatFreeIO/sampleReformatFreeIO.cpp index 4f5a6c71..98b17d0d 100644 --- a/samples/opensource/sampleReformatFreeIO/sampleReformatFreeIO.cpp +++ b/samples/opensource/sampleReformatFreeIO/sampleReformatFreeIO.cpp @@ -33,7 +33,6 @@ #include "NvInfer.h" #include -#include #include #include #include @@ -46,6 +45,8 @@ #include #include +using samplesCommon::SampleUniquePtr; + const std::string gSampleName = "TensorRT.sample_reformat_free_io"; int divUp(int a, int b) @@ -203,9 +204,6 @@ public: //! class SampleReformatFreeIO { - template - using SampleUniquePtr = std::unique_ptr; - public: SampleReformatFreeIO(const samplesCommon::CaffeSampleParams& params) : mParams(params) @@ -288,7 +286,7 @@ bool SampleReformatFreeIO::build(int dataWidth) return false; } - auto network = SampleUniquePtr(builder->createNetwork()); + auto network = SampleUniquePtr(builder->createNetworkV2(0)); if (!network) { return false; @@ -332,21 +330,40 @@ bool SampleReformatFreeIO::build(int dataWidth) config->setFlag(BuilderFlag::kGPU_FALLBACK); config->setFlag(BuilderFlag::kSTRICT_TYPES); - mEngine = std::shared_ptr( - builder->buildEngineWithConfig(*network, *config), samplesCommon::InferDeleter()); + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { + return false; + } + config->setProfileStream(*profileStream); + SampleUniquePtr plan{builder->buildSerializedNetwork(*network, *config)}; + if (!plan) + { + return false; + } + + SampleUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + if (!runtime) + { + return false; + } + + mEngine = std::shared_ptr( + runtime->deserializeCudaEngine(plan->data(), plan->size()), samplesCommon::InferDeleter()); if (!mEngine) { return false; } - assert(network->getNbInputs() == 1); + ASSERT(network->getNbInputs() == 1); mInputDims = network->getInput(0)->getDimensions(); - assert(mInputDims.nbDims == 3); + ASSERT(mInputDims.nbDims == 3); - assert(network->getNbOutputs() == 1); + ASSERT(network->getNbOutputs() == 1); mOutputDims = network->getOutput(0)->getDimensions(); - assert(mOutputDims.nbDims == 3); + ASSERT(mOutputDims.nbDims == 3); return true; } @@ -381,7 +398,7 @@ void SampleReformatFreeIO::constructNetwork( auto mean = network->addConstant(nvinfer1::Dims3(1, inputDims.d[1], inputDims.d[2]), meanWeights); auto meanSub = network->addElementWise(*network->getInput(0), *mean->getOutput(0), ElementWiseOperation::kSUB); network->getLayer(0)->setInput(0, *meanSub->getOutput(0)); - samplesCommon::setAllTensorScales(network.get(), maxMean / maxMean * 128, 128); + samplesCommon::setAllDynamicRanges(network.get(), maxMean / maxMean * 128, 128); } //! diff --git a/samples/opensource/sampleSSD/sampleSSD.cpp b/samples/opensource/sampleSSD/sampleSSD.cpp index bbe990e4..cd6fcf8f 100644 --- a/samples/opensource/sampleSSD/sampleSSD.cpp +++ b/samples/opensource/sampleSSD/sampleSSD.cpp @@ -38,6 +38,8 @@ #include #include +using samplesCommon::SampleUniquePtr; + const std::string gSampleName = "TensorRT.sample_ssd"; //! @@ -59,9 +61,6 @@ struct SampleSSDParams : public samplesCommon::CaffeSampleParams //! class SampleSSD { - template - using SampleUniquePtr = std::unique_ptr; - public: SampleSSD(const SampleSSDParams& params) : mParams(params) @@ -129,7 +128,7 @@ bool SampleSSD::build() return false; } - auto network = SampleUniquePtr(builder->createNetwork()); + auto network = SampleUniquePtr(builder->createNetworkV2(0)); if (!network) { return false; @@ -153,9 +152,9 @@ bool SampleSSD::build() return false; } - assert(network->getNbInputs() == 1); + ASSERT(network->getNbInputs() == 1); mInputDims = network->getInput(0)->getDimensions(); - assert(mInputDims.nbDims == 3); + ASSERT(mInputDims.nbDims == 3); return true; } @@ -203,8 +202,29 @@ bool SampleSSD::constructNetwork(SampleUniquePtr& builder, } samplesCommon::enableDLA(builder.get(), config.get(), mParams.dlaCore); + + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { + return false; + } + config->setProfileStream(*profileStream); + + SampleUniquePtr plan{builder->buildSerializedNetwork(*network, *config)}; + if (!plan) + { + return false; + } + + SampleUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + if (!runtime) + { + return false; + } + mEngine = std::shared_ptr( - builder->buildEngineWithConfig(*network, *config), samplesCommon::InferDeleter()); + runtime->deserializeCudaEngine(plan->data(), plan->size()), samplesCommon::InferDeleter()); if (!mEngine) { return false; @@ -231,7 +251,7 @@ bool SampleSSD::infer() } // Read the input data into the managed buffers - assert(mParams.inputTensorNames.size() == 1); + ASSERT(mParams.inputTensorNames.size() == 1); if (!processInput(buffers)) { return false; @@ -283,7 +303,7 @@ bool SampleSSD::processInput(const samplesCommon::BufferManager& buffers) // Available images std::vector imageList = {"bus.ppm"}; mPPMs.resize(batchSize); - assert(mPPMs.size() <= imageList.size()); + ASSERT(mPPMs.size() <= imageList.size()); for (int i = 0; i < batchSize; ++i) { readPPMFile(locateFile(imageList[i], mParams.dataDirs), mPPMs[i]); @@ -343,7 +363,7 @@ bool SampleSSD::verifyOutput(const samplesCommon::BufferManager& buffers) { continue; } - assert((int) det[1] < outputClsSize); + ASSERT((int) det[1] < outputClsSize); std::string storeName = classes[(int) det[1]] + "-" + std::to_string(det[2]) + ".ppm"; numDetections++; diff --git a/samples/opensource/sampleUffFasterRCNN/config.py b/samples/opensource/sampleUffFasterRCNN/config.py index 8e751585..8fbc8a71 100644 --- a/samples/opensource/sampleUffFasterRCNN/config.py +++ b/samples/opensource/sampleUffFasterRCNN/config.py @@ -1,4 +1,3 @@ -# # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - import tensorflow as tf import graphsurgeon as gs diff --git a/samples/opensource/sampleUffFasterRCNN/download_model.sh b/samples/opensource/sampleUffFasterRCNN/download_model.sh index 9404d023..a4addbe6 100755 --- a/samples/opensource/sampleUffFasterRCNN/download_model.sh +++ b/samples/opensource/sampleUffFasterRCNN/download_model.sh @@ -19,8 +19,8 @@ set -eo pipefail # check for wget which wget || { echo 'wget not found, please install.' && exit 1; } # download -mkdir -p faster-rcnn && \ -cd faster-rcnn && \ +mkdir -p uff_faster_rcnn && \ +cd uff_faster_rcnn && \ wget 'https://raw.githubusercontent.com/NVIDIA-AI-IOT/deepstream_4.x_apps/master/models/frcnn/faster_rcnn.pb' && \ wget 'https://raw.githubusercontent.com/NVIDIA-AI-IOT/deepstream_4.x_apps/master/models/frcnn/2015_0502_034830_005_00001_rain_000179.ppm' && \ wget 'https://raw.githubusercontent.com/NVIDIA-AI-IOT/deepstream_4.x_apps/master/models/frcnn/2016_1111_185016_003_00001_night_000441.ppm' && \ diff --git a/samples/opensource/sampleUffFasterRCNN/frcnnUtils.h b/samples/opensource/sampleUffFasterRCNN/frcnnUtils.h index 2a56ce59..3d578ec4 100644 --- a/samples/opensource/sampleUffFasterRCNN/frcnnUtils.h +++ b/samples/opensource/sampleUffFasterRCNN/frcnnUtils.h @@ -21,7 +21,6 @@ #include "argsParser.h" #include "common.h" #include -#include #include #include @@ -200,16 +199,16 @@ public: , mDataDir(directories) { FILE* file = fopen(locateFile(mPrefix + std::string("0") + mSuffix, mDataDir).c_str(), "rb"); - assert(file != nullptr); + ASSERT(file != nullptr); int d[4]; size_t readSize = fread(d, sizeof(int), 4, file); - assert(readSize == 4); + ASSERT(readSize == 4); mDims.nbDims = 4; // The number of dimensions. mDims.d[0] = d[0]; // Batch Size mDims.d[1] = d[1]; // Channels mDims.d[2] = d[2]; // Height mDims.d[3] = d[3]; // Width - assert(mDims.d[0] > 0 && mDims.d[1] > 0 && mDims.d[2] > 0 && mDims.d[3] > 0); + ASSERT(mDims.d[0] > 0 && mDims.d[1] > 0 && mDims.d[2] > 0 && mDims.d[3] > 0); fclose(file); mImageSize = mDims.d[1] * mDims.d[2] * mDims.d[3]; mBatch.resize(mBatchSize * mImageSize, 0); @@ -259,7 +258,7 @@ public: for (int csize = 1, batchPos = 0; batchPos < mBatchSize; batchPos += csize, mFileBatchPos += csize) { - assert(mFileBatchPos > 0 && mFileBatchPos <= mDims.d[0]); + ASSERT(mFileBatchPos > 0 && mFileBatchPos <= mDims.d[0]); if (mFileBatchPos == mDims.d[0] && !update()) { @@ -346,12 +345,12 @@ private: int d[4]; size_t readSize = fread(d, sizeof(int), 4, file); - assert(readSize == 4); - assert(mDims.d[0] == d[0] && mDims.d[1] == d[1] && mDims.d[2] == d[2] && mDims.d[3] == d[3]); + ASSERT(readSize == 4); + ASSERT(mDims.d[0] == d[0] && mDims.d[1] == d[1] && mDims.d[2] == d[2] && mDims.d[3] == d[3]); size_t readInputCount = fread(getFileBatch(), sizeof(float), mDims.d[0] * mImageSize, file); - assert(readInputCount == size_t(mDims.d[0] * mImageSize)); + ASSERT(readInputCount == size_t(mDims.d[0] * mImageSize)); size_t readLabelCount = fread(getFileLabels(), sizeof(float), mDims.d[0], file); - assert(readLabelCount == 0 || readLabelCount == size_t(mDims.d[0])); + ASSERT(readLabelCount == 0 || readLabelCount == size_t(mDims.d[0])); fclose(file); } else @@ -449,12 +448,12 @@ public: CHECK(cudaFree(mDeviceInput)); } - int getBatchSize() const + int getBatchSize() const noexcept { return mStream.getBatchSize(); } - bool getBatch(void* bindings[], const char* names[], int nbBindings) + bool getBatch(void* bindings[], const char* names[], int nbBindings) noexcept { if (!mStream.next()) { @@ -462,12 +461,12 @@ public: } CHECK(cudaMemcpy(mDeviceInput, mStream.getBatch(), mInputCount * sizeof(float), cudaMemcpyHostToDevice)); - assert(!strcmp(names[0], mInputBlobName)); + ASSERT(!strcmp(names[0], mInputBlobName)); bindings[0] = mDeviceInput; return true; } - const void* readCalibrationCache(size_t& length) + const void* readCalibrationCache(size_t& length) noexcept { mCalibrationCache.clear(); std::ifstream input(mCalibrationTableName, std::ios::binary); @@ -483,7 +482,7 @@ public: return length ? mCalibrationCache.data() : nullptr; } - void writeCalibrationCache(const void* cache, size_t length) + void writeCalibrationCache(const void* cache, size_t length) noexcept { std::ofstream output(mCalibrationTableName, std::ios::binary); output.write(reinterpret_cast(cache), length); @@ -513,22 +512,22 @@ public: { } - int getBatchSize() const override + int getBatchSize() const noexcept override { return mImpl.getBatchSize(); } - bool getBatch(void* bindings[], const char* names[], int nbBindings) override + bool getBatch(void* bindings[], const char* names[], int nbBindings) noexcept override { return mImpl.getBatch(bindings, names, nbBindings); } - const void* readCalibrationCache(size_t& length) override + const void* readCalibrationCache(size_t& length) noexcept override { return mImpl.readCalibrationCache(length); } - void writeCalibrationCache(const void* cache, size_t length) override + void writeCalibrationCache(const void* cache, size_t length) noexcept override { mImpl.writeCalibrationCache(cache, length); } diff --git a/samples/opensource/sampleUffFasterRCNN/sampleUffFasterRCNN.cpp b/samples/opensource/sampleUffFasterRCNN/sampleUffFasterRCNN.cpp index 182393bc..3592a5f1 100644 --- a/samples/opensource/sampleUffFasterRCNN/sampleUffFasterRCNN.cpp +++ b/samples/opensource/sampleUffFasterRCNN/sampleUffFasterRCNN.cpp @@ -30,7 +30,6 @@ #include "frcnnUtils.h" #include "logger.h" #include -#include #include #include #include @@ -43,6 +42,7 @@ #include using namespace samplesCommon; +using samplesCommon::SampleUniquePtr; //! \brief Define the PPM objects as global variable. //! @@ -92,9 +92,6 @@ struct SampleUffFasterRcnnParams : public samplesCommon::SampleParams //! class SampleUffFasterRcnn { - template - using SampleUniquePtr = std::unique_ptr; - public: SampleUffFasterRcnn(const SampleUffFasterRcnnParams& params) : mParams(params) @@ -201,18 +198,12 @@ bool SampleUffFasterRcnn::build() auto builder = SampleUniquePtr(nvinfer1::createInferBuilder(sample::gLogger.getTRTLogger())); - if (mParams.dlaCore >= 0) - { - builder->setDefaultDeviceType(nvinfer1::DeviceType::kDLA); - builder->setDLACore(mParams.dlaCore); - builder->allowGPUFallback(true); - } if (!builder) { return false; } - auto network = SampleUniquePtr(builder->createNetwork()); + auto network = SampleUniquePtr(builder->createNetworkV2(0)); if (!network) { @@ -233,18 +224,20 @@ bool SampleUffFasterRcnn::build() return false; } - assert(network->getNbInputs() == 1); + ASSERT(network->getNbInputs() == 1); mInputDims = network->getInput(0)->getDimensions(); - assert(mInputDims.nbDims == 3); - assert(network->getNbOutputs() == 3); + ASSERT(mInputDims.nbDims == 3); + ASSERT(network->getNbOutputs() == 3); return true; } bool SampleUffFasterRcnn::constructNetwork(SampleUniquePtr& builder, SampleUniquePtr& network, SampleUniquePtr& parser) { + SampleUniquePtr config{builder->createBuilderConfig()}; + parser->registerInput(mParams.inputNodeName.c_str(), - DimsCHW(mParams.inputChannels, mParams.inputHeight, mParams.inputWidth), nvuffparser::UffInputOrder::kNCHW); + Dims3(mParams.inputChannels, mParams.inputHeight, mParams.inputWidth), nvuffparser::UffInputOrder::kNCHW); parser->registerOutput(mParams.outputRegName.c_str()); parser->registerOutput(mParams.outputClsName.c_str()); parser->registerOutput(mParams.outputProposalName.c_str()); @@ -256,10 +249,18 @@ bool SampleUffFasterRcnn::constructNetwork(SampleUniquePtr& } builder->setMaxBatchSize(mParams.batchSize); - builder->setMaxWorkspaceSize(2_GiB); + config->setMaxWorkspaceSize(2_GiB); + + if (mParams.dlaCore >= 0) + { + config->setDefaultDeviceType(nvinfer1::DeviceType::kDLA); + config->setDLACore(mParams.dlaCore); + config->setFlag(BuilderFlag::kGPU_FALLBACK); + } + if (mParams.fp16) { - builder->setFp16Mode(true); + config->setFlag(BuilderFlag::kFP16); } // Calibrator life time needs to last until after the engine is built. std::unique_ptr calibrator; @@ -271,25 +272,44 @@ bool SampleUffFasterRcnn::constructNetwork(SampleUniquePtr& const int imageC = 3; const int imageH = mParams.inputHeight; const int imageW = mParams.inputWidth; - nvinfer1::DimsNCHW imageDims{mParams.calBatchSize, imageC, imageH, imageW}; + nvinfer1::Dims4 imageDims{mParams.calBatchSize, imageC, imageH, imageW}; // To prevent compiler initialization warning with some versions of gcc for (int i = imageDims.nbDims; i < Dims::MAX_DIMS; ++i) { imageDims.d[i] = 0; - imageDims.type[i] = DimensionType::kSPATIAL; } BatchStream calibrationStream( mParams.calBatchSize, mParams.nbCalBatches, imageDims, listFileName, mParams.dataDirs); calibrator.reset( new Int8EntropyCalibrator2(calibrationStream, 0, "UffFasterRcnn", mParams.inputNodeName.c_str())); - builder->setInt8Mode(true); + config->setFlag(BuilderFlag::kINT8); // Fallback to FP16 if there is no INT8 kernels. - builder->setFp16Mode(true); - builder->setInt8Calibrator(calibrator.get()); + config->setFlag(BuilderFlag::kFP16); + config->setInt8Calibrator(calibrator.get()); } - mEngine = std::shared_ptr(builder->buildCudaEngine(*network), samplesCommon::InferDeleter()); + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { + return false; + } + config->setProfileStream(*profileStream); + SampleUniquePtr plan{builder->buildSerializedNetwork(*network, *config)}; + if (!plan) + { + return false; + } + + SampleUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + if (!runtime) + { + return false; + } + + mEngine = std::shared_ptr( + runtime->deserializeCudaEngine(plan->data(), plan->size()), samplesCommon::InferDeleter()); if (!mEngine) { return false; @@ -303,7 +323,7 @@ bool SampleUffFasterRcnn::constructNetwork(SampleUniquePtr& return false; } nvinfer1::IHostMemory* ptr = mEngine->serialize(); - assert(ptr); + ASSERT(ptr); p.write(reinterpret_cast(ptr->data()), ptr->size()); ptr->destroy(); p.close(); @@ -383,7 +403,7 @@ bool SampleUffFasterRcnn::processInput(const samplesCommon::BufferManager& buffe const int batchSize = mParams.batchSize; std::vector imageList = mParams.inputImages; ppms.resize(batchSize); - assert(ppms.size() <= imageList.size()); + ASSERT(ppms.size() <= imageList.size()); for (int i = 0; i < batchSize; ++i) { @@ -451,7 +471,7 @@ SampleUffFasterRcnnParams initializeSampleParams(const FrcnnArgs& args) params.dataDirs.push_back("data/samples/faster-rcnn/"); } - assert(args.batchSize == static_cast(args.inputImages.size())); + ASSERT(args.batchSize == static_cast(args.inputImages.size())); params.inputImages = args.inputImages; params.uffFileName = "faster_rcnn.uff"; params.inputNodeName = "input_1"; diff --git a/samples/opensource/sampleUffMNIST/sampleUffMNIST.cpp b/samples/opensource/sampleUffMNIST/sampleUffMNIST.cpp index 2110f792..f7034da0 100644 --- a/samples/opensource/sampleUffMNIST/sampleUffMNIST.cpp +++ b/samples/opensource/sampleUffMNIST/sampleUffMNIST.cpp @@ -33,7 +33,6 @@ #include #include -#include #include #include #include @@ -43,6 +42,8 @@ #include #include +using samplesCommon::SampleUniquePtr; + const std::string gSampleName = "TensorRT.sample_uff_mnist"; //! @@ -52,9 +53,6 @@ const std::string gSampleName = "TensorRT.sample_uff_mnist"; //! class SampleUffMNIST { - template - using SampleUniquePtr = std::unique_ptr; - public: SampleUffMNIST(const samplesCommon::UffSampleParams& params) : mParams(params) @@ -119,7 +117,7 @@ bool SampleUffMNIST::build() { return false; } - auto network = SampleUniquePtr(builder->createNetwork()); + auto network = SampleUniquePtr(builder->createNetworkV2(0)); if (!network) { return false; @@ -149,16 +147,36 @@ bool SampleUffMNIST::build() samplesCommon::enableDLA(builder.get(), config.get(), mParams.dlaCore); - mEngine = std::shared_ptr( - builder->buildEngineWithConfig(*network, *config), samplesCommon::InferDeleter()); + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { + return false; + } + config->setProfileStream(*profileStream); + SampleUniquePtr plan{builder->buildSerializedNetwork(*network, *config)}; + if (!plan) + { + return false; + } + + SampleUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + if (!runtime) + { + return false; + } + + mEngine = std::shared_ptr( + runtime->deserializeCudaEngine(plan->data(), plan->size()), samplesCommon::InferDeleter()); if (!mEngine) { return false; } - assert(network->getNbInputs() == 1); + + ASSERT(network->getNbInputs() == 1); mInputDims = network->getInput(0)->getDimensions(); - assert(mInputDims.nbDims == 3); + ASSERT(mInputDims.nbDims == 3); return true; } @@ -174,8 +192,8 @@ void SampleUffMNIST::constructNetwork( SampleUniquePtr& parser, SampleUniquePtr& network) { // There should only be one input and one output tensor - assert(mParams.inputTensorNames.size() == 1); - assert(mParams.outputTensorNames.size() == 1); + ASSERT(mParams.inputTensorNames.size() == 1); + ASSERT(mParams.outputTensorNames.size() == 1); // Register tensorflow input parser->registerInput( @@ -186,7 +204,7 @@ void SampleUffMNIST::constructNetwork( if (mParams.int8) { - samplesCommon::setAllTensorScales(network.get(), 127.0f, 127.0f); + samplesCommon::setAllDynamicRanges(network.get(), 127.0f, 127.0f); } } diff --git a/samples/opensource/sampleUffMaskRCNN/converted/config.py b/samples/opensource/sampleUffMaskRCNN/converted/config.py index c4c01a7b..087bc1f7 100644 --- a/samples/opensource/sampleUffMaskRCNN/converted/config.py +++ b/samples/opensource/sampleUffMaskRCNN/converted/config.py @@ -126,3 +126,4 @@ def preprocess(dynamic_graph): connect(dynamic_graph, timedistributed_connect_pairs) connect(dynamic_graph, dense_compatible_connect_pairs) + diff --git a/samples/opensource/sampleUffMaskRCNN/sampleUffMaskRCNN.cpp b/samples/opensource/sampleUffMaskRCNN/sampleUffMaskRCNN.cpp index 9b1e97e8..5eebbad5 100644 --- a/samples/opensource/sampleUffMaskRCNN/sampleUffMaskRCNN.cpp +++ b/samples/opensource/sampleUffMaskRCNN/sampleUffMaskRCNN.cpp @@ -15,11 +15,10 @@ */ #ifndef _MSC_VER -#include #include +#include #endif -#include #include #include #include @@ -45,6 +44,8 @@ // MaskRCNN Parameter #include "mrcnn_config.h" +using samplesCommon::SampleUniquePtr; + const std::string gSampleName = "TensorRT.sample_maskrcnn"; namespace MaskRCNNUtils @@ -80,7 +81,7 @@ void readPPMFile(const std::string& filename, PPM& ppm) { ppm.fileName = filename; std::ifstream infile(filename, std::ifstream::binary); - assert(infile.is_open() && "Attempting to read from a file that is not open. "); + ASSERT(infile.is_open() && "Attempting to read from a file that is not open. "); infile >> ppm.magic >> ppm.w >> ppm.h >> ppm.max; infile.seekg(1, infile.cur); @@ -92,7 +93,7 @@ void readPPMFile(const std::string& filename, PPM& ppm) void writePPMFile(const std::string& filename, PPM& ppm) { std::ofstream outfile("./" + filename, std::ofstream::binary); - assert(!outfile.fail()); + ASSERT(!outfile.fail()); outfile << "P6" << "\n" << ppm.w << " " << ppm.h << "\n" @@ -107,8 +108,8 @@ void resizePPM(const PPM& src, PPM& dst, int target_height, int target_wid auto clip = [](float in, float low, float high) -> float { return (in < low) ? low : (in > high ? high : in); }; int original_height = src.h; int original_width = src.w; - assert(dst.h == target_height); - assert(dst.w == target_width); + ASSERT(dst.h == target_height); + ASSERT(dst.w == target_width); float ratio_h = static_cast(original_height - 1.0f) / static_cast(target_height - 1.0f); float ratio_w = static_cast(original_width - 1.0f) / static_cast(target_width - 1.0f); @@ -143,8 +144,8 @@ void resizePPM(const PPM& src, PPM& dst, int target_height, int target_wid void padPPM(const PPM& src, PPM& dst, int top, int bottom, int left, int right) { - assert(dst.h == (src.h + top + bottom)); - assert(dst.w == (src.w + left + right)); + ASSERT(dst.h == (src.h + top + bottom)); + ASSERT(dst.w == (src.w + left + right)); for (int y = 0; y < src.h; y++) { @@ -160,13 +161,13 @@ void padPPM(const PPM& src, PPM& dst, int top, int bottom, int void preprocessPPM(PPM& src, PPM& dst, int target_h, int target_w) { - assert(target_h == target_w); + ASSERT(target_h == target_w); int input_dim = target_h; // padding the input img to model's input_size: const int image_dim = std::max(src.h, src.w); int resize_h = src.h * input_dim / image_dim; int resize_w = src.w * input_dim / image_dim; - assert(resize_h == input_dim || resize_w == input_dim); + ASSERT(resize_h == input_dim || resize_w == input_dim); int y_offset = (input_dim - resize_h) / 2; int x_offset = (input_dim - resize_w) / 2; @@ -192,7 +193,7 @@ PPM resizeMask(const BBoxInfo& box, const float mask_threshold) PPM result; if (!box.mask) { - assert(result.buffer.size() == 0); + ASSERT(result.buffer.size() == 0); return result; } @@ -240,8 +241,8 @@ void maskPPM( uint8_t mask_pixel = mask.buffer[y * mask.w + x]; if (mask_pixel == 1) { - assert(0 <= start_y + y && start_y + y < image.h); - assert(0 <= start_x + x && start_x + x < image.w); + ASSERT(0 <= start_y + y && start_y + y < image.h); + ASSERT(0 <= start_x + x && start_x + x < image.w); int cur_y = start_y + y; int cur_x = start_x + x; @@ -258,7 +259,7 @@ void maskPPM( = static_cast(std::max(0.0f, std::min(255.0f, p_b * (1 - alpha) + color[2] * alpha))); } else - assert(mask_pixel == 0); + ASSERT(mask_pixel == 0); } } } @@ -309,9 +310,6 @@ struct SampleMaskRCNNParams : public samplesCommon::SampleParams class SampleMaskRCNN { - template - using SampleUniquePtr = std::unique_ptr; - public: SampleMaskRCNN(const SampleMaskRCNNParams& params) : mParams(params) @@ -358,7 +356,7 @@ bool SampleMaskRCNN::build() return false; } - auto network = SampleUniquePtr(builder->createNetwork()); + auto network = SampleUniquePtr(builder->createNetworkV2(0)); if (!network) { return false; @@ -376,11 +374,11 @@ bool SampleMaskRCNN::build() return false; } - assert(network->getNbInputs() == 1); + ASSERT(network->getNbInputs() == 1); mInputDims = network->getInput(0)->getDimensions(); - assert(mInputDims.nbDims == 3); + ASSERT(mInputDims.nbDims == 3); - assert(network->getNbOutputs() == 2); + ASSERT(network->getNbOutputs() == 2); return true; } @@ -399,18 +397,44 @@ bool SampleMaskRCNN::constructNetwork(SampleUniquePtr& build return false; } + SampleUniquePtr config{builder->createBuilderConfig()}; + builder->setMaxBatchSize(mParams.batchSize); - builder->setMaxWorkspaceSize(1_GiB); - builder->setFp16Mode(mParams.fp16); + config->setMaxWorkspaceSize(1_GiB); + if (mParams.fp16) + { + config->setFlag(BuilderFlag::kFP16); + } // Only for speed test if (mParams.int8) { - samplesCommon::setAllTensorScales(network.get()); - builder->setInt8Mode(true); + samplesCommon::setAllDynamicRanges(network.get()); + config->setFlag(BuilderFlag::kINT8); } - mEngine = std::shared_ptr(builder->buildCudaEngine(*network), samplesCommon::InferDeleter()); + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { + return false; + } + config->setProfileStream(*profileStream); + + SampleUniquePtr plan{builder->buildSerializedNetwork(*network, *config)}; + if (!plan) + { + return false; + } + + SampleUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + if (!runtime) + { + return false; + } + + mEngine = std::shared_ptr( + runtime->deserializeCudaEngine(plan->data(), plan->size()), samplesCommon::InferDeleter()); if (!mEngine) { return false; @@ -431,7 +455,7 @@ bool SampleMaskRCNN::infer() } // Read the input data into the managed buffers - assert(mParams.inputTensorNames.size() == 1); + ASSERT(mParams.inputTensorNames.size() == 1); if (!processInput(buffers)) { return false; @@ -449,8 +473,7 @@ bool SampleMaskRCNN::infer() auto tEnd = std::chrono::high_resolution_clock::now(); float totalHost = std::chrono::duration(tEnd - tStart).count(); sample::gLogInfo << "Run for 10 times with Batch Size " << mParams.batchSize << std::endl; - sample::gLogInfo << "Average inference time is " << (totalHost / 10) / mParams.batchSize << " ms/frame" - << std::endl; + sample::gLogInfo << "Average inference time is " << (totalHost / 10) / mParams.batchSize << " ms/frame" << std::endl; if (!status) { @@ -495,7 +518,7 @@ bool SampleMaskRCNN::processInput(const samplesCommon::BufferManager& buffers) mPPMs.resize(batchSize); mOriginalPPMs.resize(batchSize); - assert(mPPMs.size() <= imageList.size()); + ASSERT(mPPMs.size() <= imageList.size()); for (int i = 0; i < batchSize; ++i) { MaskRCNNUtils::readPPMFile(locateFile(imageList[i], mParams.dataDirs), mOriginalPPMs[i]); @@ -524,7 +547,7 @@ std::vector SampleMaskRCNN::decodeOutput( const int imageIdx, void* detectionsHost, void* masksHost) { int input_dim_h = MaskRCNNConfig::IMAGE_SHAPE.d[1], input_dim_w = MaskRCNNConfig::IMAGE_SHAPE.d[2]; - assert(input_dim_h == input_dim_w); + ASSERT(input_dim_h == input_dim_w); int image_height = mOriginalPPMs[imageIdx].h; int image_width = mOriginalPPMs[imageIdx].w; // resize the DsImage with scale @@ -591,9 +614,9 @@ bool SampleMaskRCNN::verifyOutput(const samplesCommon::BufferManager& buffers) MaskRCNNUtils::addBBoxPPM(mOriginalPPMs[p], binfo[roi_id], resized_mask); sample::gLogInfo << "Detected " << MaskRCNNConfig::CLASS_NAMES[binfo[roi_id].label] << " in" - << mOriginalPPMs[p].fileName << " with confidence " << binfo[roi_id].prob * 100.f - << " and coordinates (" << binfo[roi_id].box.x1 << ", " << binfo[roi_id].box.y1 << ", " - << binfo[roi_id].box.x2 << ", " << binfo[roi_id].box.y2 << ")" << std::endl; + << mOriginalPPMs[p].fileName << " with confidence " << binfo[roi_id].prob * 100.f + << " and coordinates (" << binfo[roi_id].box.x1 << ", " << binfo[roi_id].box.y1 << ", " + << binfo[roi_id].box.x2 << ", " << binfo[roi_id].box.y2 << ")" << std::endl; } sample::gLogInfo << "The results are stored in current directory: " << std::to_string(p) + ".ppm" << std::endl; MaskRCNNUtils::writePPMFile(std::to_string(p) + ".ppm", mOriginalPPMs[p]); diff --git a/samples/opensource/sampleUffPluginV2Ext/sampleUffPluginV2Ext.cpp b/samples/opensource/sampleUffPluginV2Ext/sampleUffPluginV2Ext.cpp index 5cd8a2ba..06e98d57 100755 --- a/samples/opensource/sampleUffPluginV2Ext/sampleUffPluginV2Ext.cpp +++ b/samples/opensource/sampleUffPluginV2Ext/sampleUffPluginV2Ext.cpp @@ -16,9 +16,7 @@ #include "NvInfer.h" #include "NvUffParser.h" -#include #include -#include #include #include #include @@ -42,39 +40,39 @@ samplesCommon::Args gArgs; template void transform(const void* src, void* dst, int count) { - assert(in == out); + ASSERT(in == out); memcpy(dst, src, count * elementSize(in)); } template <> void transform(const void* src, void* dst, int count) { - auto srcPtr = static_cast(src); - auto dstPtr = static_cast(dst); + const auto* srcPtr = static_cast(src); + auto* dstPtr = static_cast(dst); std::transform(srcPtr, srcPtr + count, dstPtr, [](half_float::half in) { return static_cast(in); }); } template <> void transform(const void* src, void* dst, int count) { - auto srcPtr = static_cast(src); - auto dstPtr = static_cast(dst); + const auto* srcPtr = static_cast(src); + auto* dstPtr = static_cast(dst); std::transform(srcPtr, srcPtr + count, dstPtr, [](int8_t in) { return static_cast(in); }); } template <> void transform(const void* src, void* dst, int count) { - auto srcPtr = static_cast(src); - auto dstPtr = static_cast(dst); + const auto* srcPtr = static_cast(src); + auto* dstPtr = static_cast(dst); std::transform(srcPtr, srcPtr + count, dstPtr, [](float in) { return static_cast(in); }); } template <> void transform(const void* src, void* dst, int count) { - auto srcPtr = static_cast(src); - auto dstPtr = static_cast(dst); + const auto* srcPtr = static_cast(src); + auto* dstPtr = static_cast(dst); std::transform(srcPtr, srcPtr + count, dstPtr, [](float x) { x = std::max(x, float(INT8_MIN)); x = std::min(x, float(INT8_MAX)); @@ -109,8 +107,8 @@ std::vector> calculateBindingBufferSizes( void* createMnistCudaBuffer(int64_t eltCount, DataType dtype, int num) { // in that specific case, eltCount == INPUT_H * INPUT_W - assert(eltCount == INPUT_H * INPUT_W); - assert(elementSize(dtype) == sizeof(float)); + ASSERT(eltCount == INPUT_H * INPUT_W); + ASSERT(elementSize(dtype) == sizeof(float)); size_t memSize = eltCount * elementSize(dtype); std::vector inputs(eltCount); @@ -141,7 +139,7 @@ void* createMnistCudaBuffer(int64_t eltCount, DataType dtype, int num) bool verifyOutput(int64_t eltCount, DataType dtype, void* buffer, int num) { - assert(elementSize(dtype) == sizeof(float)); + ASSERT(elementSize(dtype) == sizeof(float)); bool pass = false; @@ -161,7 +159,7 @@ bool verifyOutput(int64_t eltCount, DataType dtype, void* buffer, int num) if (eltIdx == maxIdx) { sample::gLogInfo << "***"; - pass = eltIdx == num ? true : false; + pass = eltIdx == num; } sample::gLogInfo << "\n"; } @@ -189,9 +187,6 @@ struct PoolParameters class SampleUffPluginV2Ext { public: - template - using SampleUniquePtr = std::unique_ptr; - explicit SampleUffPluginV2Ext(const UffSampleParams& params) : mParams(params) { @@ -213,7 +208,7 @@ public: return false; } - SampleUniquePtr network{builder->createNetwork()}; + SampleUniquePtr network{builder->createNetworkV2(0)}; if (!network.get()) { sample::gLogError << "Failed to create network. " << std::endl; @@ -228,7 +223,7 @@ public: if (gArgs.runInInt8) { - samplesCommon::setAllTensorScales(network.get(), 25.0f, 25.0f); + samplesCommon::setAllDynamicRanges(network.get(), 25.0F, 25.0F); } SampleUniquePtr networkConfig{builder->createBuilderConfig()}; @@ -251,7 +246,29 @@ public: builder->setMaxBatchSize(maxBatchSize); samplesCommon::enableDLA(builder.get(), networkConfig.get(), gArgs.useDLACore); - mEngine.reset(builder->buildEngineWithConfig(*network, *networkConfig)); + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { + return false; + } + networkConfig->setProfileStream(*profileStream); + + SampleUniquePtr plan{builder->buildSerializedNetwork(*network, *networkConfig)}; + if (!plan) + { + sample::gLogError << "Unable to create serialized engine. " << std::endl; + return false; + } + + SampleUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + if (!runtime) + { + sample::gLogError << "Unable to create runtime. " << std::endl; + return false; + } + + mEngine.reset(runtime->deserializeCudaEngine(plan->data(), plan->size())); if (!mEngine.get()) { sample::gLogError << "Unable to create engine. " << std::endl; @@ -270,7 +287,7 @@ public: const int batchSize{1}; const int nbBindings = mEngine->getNbBindings(); - assert(nbBindings == 2); + ASSERT(nbBindings == 2); std::vector buffers(nbBindings); auto buffersSizes = calculateBindingBufferSizes(*mEngine, nbBindings, batchSize); @@ -286,7 +303,7 @@ public: const int numberRun{10}; for (int i = 0; i < iterations; i++) { - float total{0.0f}, ms{0.0f}; + float total{0.0F}, ms{0.0F}; for (int num = 0; num < numberRun; num++) { buffers[bindingIdxInput] = createMnistCudaBuffer(bufferSizesInput.first, bufferSizesInput.second, num); @@ -375,7 +392,7 @@ public: mInHostScale = read(d); mOutHostScale = read(d); } - assert(d == a + length); + ASSERT(d == a + length); } // It makes no sense to construct UffPoolPluginV2 without arguments. @@ -384,21 +401,21 @@ public: virtual ~UffPoolPluginV2() {} public: - int getNbOutputs() const override + int getNbOutputs() const noexcept override { return 1; } - Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) override + Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) noexcept override { - assert(index == 0 && nbInputDims == 1 && inputs[0].nbDims == 3); + ASSERT(index == 0 && nbInputDims == 1 && inputs[0].nbDims == 3); int height = (inputs[0].d[1] + mPoolingParams.pH * 2 - mPoolingParams.mR) / mPoolingParams.mU + 1; int width = (inputs[0].d[2] + mPoolingParams.pW * 2 - mPoolingParams.mS) / mPoolingParams.mV + 1; DimsHW outDims(height, width); return Dims3(inputs[0].d[0], outDims.h(), outDims.w()); } - int initialize() override + int initialize() noexcept override { CHECK(cudnnCreate(&mCudnn)); CHECK(cudnnCreateTensorDescriptor(&mSrcDescriptor)); @@ -409,7 +426,7 @@ public: return 0; } - void terminate() override + void terminate() noexcept override { CHECK(cudnnDestroyTensorDescriptor(mSrcDescriptor)); CHECK(cudnnDestroyTensorDescriptor(mDstDescriptor)); @@ -417,21 +434,22 @@ public: CHECK(cudnnDestroy(mCudnn)); } - size_t getWorkspaceSize(int maxBatchSize) const override + size_t getWorkspaceSize(int maxBatchSize) const noexcept override { return 0; } - int enqueue(int batchSize, const void* const* inputs, void** outputs, void* workspace, cudaStream_t stream) override + int enqueue(int batchSize, const void* const* inputs, void* const* outputs, void* workspace, + cudaStream_t stream) noexcept override { - const float kONE = 1.0f, kZERO = 0.0f; + const float kONE = 1.0F, kZERO = 0.0F; cudnnSetStream(mCudnn, stream); const int N = 1; // Use float to simulate int8 calculation std::map typeMap = {{DataType::kFLOAT, CUDNN_DATA_FLOAT}, {DataType::kHALF, CUDNN_DATA_HALF}, {DataType::kINT8, CUDNN_DATA_FLOAT}}; - assert(mDataType != DataType::kINT32); + ASSERT(mDataType != DataType::kINT32); CHECK(cudnnSetTensor4dDescriptor(mSrcDescriptor, CUDNN_TENSOR_NCHW, typeMap[mDataType], N, mPoolingParams.mC, mPoolingParams.mH, mPoolingParams.mW)); CHECK(cudnnSetTensor4dDescriptor(mDstDescriptor, CUDNN_TENSOR_NCHW, typeMap[mDataType], N, mPoolingParams.mC, @@ -457,7 +475,7 @@ public: return 0; } - size_t getSerializationSize() const override + size_t getSerializationSize() const noexcept override { size_t serializationSize = 0; serializationSize += sizeof(mPoolingParams); @@ -473,19 +491,19 @@ public: return serializationSize; } - void serialize(void* buffer) const override + void serialize(void* buffer) const noexcept override { char* d = static_cast(buffer); const char* const a = d; write(d, mPoolingParams); write(d, mInputDims.nbDims); - assert(mInputDims.nbDims <= mInputDims.MAX_DIMS); + ASSERT(mInputDims.nbDims <= mInputDims.MAX_DIMS); for (int i = 0; i < mInputDims.nbDims; ++i) { write(d, mInputDims.d[i]); } write(d, mOutputDims.nbDims); - assert(mOutputDims.nbDims <= mOutputDims.MAX_DIMS); + ASSERT(mOutputDims.nbDims <= mOutputDims.MAX_DIMS); for (int i = 0; i < mOutputDims.nbDims; ++i) { write(d, mOutputDims.d[i]); @@ -496,15 +514,15 @@ public: write(d, mInHostScale); write(d, mOutHostScale); } - assert(d == a + getSerializationSize()); + ASSERT(d == a + getSerializationSize()); } - void configurePlugin(const PluginTensorDesc* in, int nbInput, const PluginTensorDesc* out, int nbOutput) override + void configurePlugin(const PluginTensorDesc* in, int nbInput, const PluginTensorDesc* out, int nbOutput) noexcept override { - assert(in && nbInput == 1); - assert(out && nbOutput == 1); - assert(in[0].type == out[0].type); - assert(in[0].format == TensorFormat::kLINEAR && out[0].format == TensorFormat::kLINEAR); + ASSERT(in && nbInput == 1); + ASSERT(out && nbOutput == 1); + ASSERT(in[0].type == out[0].type); + ASSERT(in[0].format == TensorFormat::kLINEAR && out[0].format == TensorFormat::kLINEAR); mDataType = in[0].type; mInputDims = in[0].dims; @@ -514,88 +532,87 @@ public: mPoolingParams.mW = mInputDims.d[2]; mPoolingParams.mP = mOutputDims.d[1]; mPoolingParams.mQ = mOutputDims.d[2]; - mInHostScale = in[0].scale >= 0.0f ? in[0].scale : -1.0f; - mOutHostScale = out[0].scale >= 0.0f ? out[0].scale : -1.0f; + mInHostScale = in[0].scale >= 0.0F ? in[0].scale : -1.0F; + mOutHostScale = out[0].scale >= 0.0F ? out[0].scale : -1.0F; } //! The combination of kLINEAR + kINT8/kHALF/kFLOAT is supported. - bool supportsFormatCombination(int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) const override + bool supportsFormatCombination(int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) const noexcept override { - assert(nbInputs == 1 && nbOutputs == 1 && pos < nbInputs + nbOutputs); + ASSERT(nbInputs == 1 && nbOutputs == 1 && pos < nbInputs + nbOutputs); bool condition = inOut[pos].format == TensorFormat::kLINEAR; condition &= inOut[pos].type != DataType::kINT32; condition &= inOut[pos].type == inOut[0].type; return condition; } - DataType getOutputDataType(int index, const DataType* inputTypes, int nbInputs) const override + DataType getOutputDataType(int index, const DataType* inputTypes, int nbInputs) const noexcept override { - assert(inputTypes && nbInputs == 1); + ASSERT(inputTypes && nbInputs == 1); (void) index; return inputTypes[0]; } - const char* getPluginType() const override + const char* getPluginType() const noexcept override { return "MaxPool"; } - const char* getPluginVersion() const override + const char* getPluginVersion() const noexcept override { return "2"; } - void destroy() override + void destroy() noexcept override { delete this; } - IPluginV2Ext* clone() const override + IPluginV2Ext* clone() const noexcept override { auto* plugin = new UffPoolPluginV2(*this); return plugin; } - void setPluginNamespace(const char* libNamespace) override + void setPluginNamespace(const char* libNamespace) noexcept override { mNamespace = libNamespace; } - const char* getPluginNamespace() const override + const char* getPluginNamespace() const noexcept override { return mNamespace.data(); } - bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const override + bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const noexcept override { return false; } - bool canBroadcastInputAcrossBatch(int inputIndex) const override + bool canBroadcastInputAcrossBatch(int inputIndex) const noexcept override { return false; } private: template - void write(char*& buffer, const T& val) const + void write(char*& buffer, const T& val) const noexcept { - std::memcpy(buffer, &val, sizeof(T)); + *reinterpret_cast(buffer) = val; buffer += sizeof(T); } template - T read(const char*& buffer) const + T read(const char*& buffer) const noexcept { - T val{}; - std::memcpy(&val, buffer, sizeof(T)); + T val = *reinterpret_cast(buffer); buffer += sizeof(T); return val; } - void copyDeviceInputToFP32(const void* src, void*& dst) + void copyDeviceInputToFP32(const void* src, void*& dst) noexcept { - assert(mDataType == DataType::kINT8); + ASSERT(mDataType == DataType::kINT8); size_t inCount = getC(mInputDims) * getH(mInputDims) * getW(mInputDims); std::vector inputTmp(inCount * elementSize(mDataType)); CHECK(cudaMemcpy(inputTmp.data(), src, inCount * elementSize(mDataType), cudaMemcpyDeviceToHost)); @@ -612,7 +629,7 @@ private: CHECK(cudaMemcpy(dst, inputFP32.data(), inCount * elementSize(DataType::kFLOAT), cudaMemcpyHostToDevice)); } - void copyDeviceToInt8Output(const void* src, void* dst) + void copyDeviceToInt8Output(const void* src, void* dst) noexcept { size_t outCount = getC(mOutputDims) * getH(mOutputDims) * getW(mOutputDims); std::vector outTmp(outCount); @@ -639,50 +656,50 @@ private: Dims mInputDims; Dims mOutputDims; - float mInHostScale{-1.0f}; - float mOutHostScale{-1.0f}; + float mInHostScale{-1.0F}; + float mOutHostScale{-1.0F}; std::string mNamespace; }; class UffPoolPluginV2Creator : public IPluginCreator { public: - const char* getPluginName() const override + const char* getPluginName() const noexcept override { return "MaxPool"; } - const char* getPluginVersion() const override + const char* getPluginVersion() const noexcept override { return "2"; } - const PluginFieldCollection* getFieldNames() override + const PluginFieldCollection* getFieldNames() noexcept override { return &mFieldCollection; } - IPluginV2* createPlugin(const char* name, const PluginFieldCollection* fc) override + IPluginV2* createPlugin(const char* name, const PluginFieldCollection* fc) noexcept override { - auto plugin = new UffPoolPluginV2(*fc); + auto* plugin = new UffPoolPluginV2(*fc); mFieldCollection = *fc; mPluginName = name; return plugin; } - IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override + IPluginV2* deserializePlugin(const char* name, const void* serialData, size_t serialLength) noexcept override { - auto plugin = new UffPoolPluginV2(serialData, serialLength); + auto* plugin = new UffPoolPluginV2(serialData, serialLength); mPluginName = name; return plugin; } - void setPluginNamespace(const char* libNamespace) override + void setPluginNamespace(const char* libNamespace) noexcept override { mNamespace = libNamespace; } - const char* getPluginNamespace() const override + const char* getPluginNamespace() const noexcept override { return mNamespace.c_str(); } @@ -730,9 +747,9 @@ int main(int argc, char** argv) { gArgs.dataDirs = std::vector{"data/samples/mnist/", "data/mnist/"}; } - auto sampleTest = sample::gLogger.defineTest(gSampleName, argc, argv); + auto sampleTest = sample::Logger::defineTest(gSampleName, argc, argv); - sample::gLogger.reportTestStart(sampleTest); + sample::Logger::reportTestStart(sampleTest); samplesCommon::UffSampleParams params; params.uffFileName = locateFile("lenet5_custom_pool.uff", gArgs.dataDirs); @@ -741,18 +758,18 @@ int main(int argc, char** argv) if (!sample.build()) { - return sample::gLogger.reportFail(sampleTest); + return sample::Logger::reportFail(sampleTest); } if (!sample.infer()) { - return sample::gLogger.reportFail(sampleTest); + return sample::Logger::reportFail(sampleTest); } if (!sample.teardown()) { - return sample::gLogger.reportFail(sampleTest); + return sample::Logger::reportFail(sampleTest); } - return sample::gLogger.reportPass(sampleTest); + return sample::Logger::reportPass(sampleTest); } diff --git a/samples/opensource/sampleUffSSD/sampleUffSSD.cpp b/samples/opensource/sampleUffSSD/sampleUffSSD.cpp index ee1ed33a..ad504f92 100644 --- a/samples/opensource/sampleUffSSD/sampleUffSSD.cpp +++ b/samples/opensource/sampleUffSSD/sampleUffSSD.cpp @@ -38,6 +38,8 @@ #include #include +using samplesCommon::SampleUniquePtr; + const std::string gSampleName = "TensorRT.sample_uff_ssd"; const std::vector gImgFnames = {"dog.ppm", "bus.ppm"}; @@ -62,9 +64,6 @@ struct SampleUffSSDParams : public samplesCommon::SampleParams //! class SampleUffSSD { - template - using SampleUniquePtr = std::unique_ptr; - public: SampleUffSSD(const SampleUffSSDParams& params) : mParams(params) @@ -132,7 +131,7 @@ bool SampleUffSSD::build() return false; } - auto network = SampleUniquePtr(builder->createNetwork()); + auto network = SampleUniquePtr(builder->createNetworkV2(0)); if (!network) { return false; @@ -156,11 +155,11 @@ bool SampleUffSSD::build() return false; } - assert(network->getNbInputs() == 1); + ASSERT(network->getNbInputs() == 1); mInputDims = network->getInput(0)->getDimensions(); - assert(mInputDims.nbDims == 3); + ASSERT(mInputDims.nbDims == 3); - assert(network->getNbOutputs() == 2); + ASSERT(network->getNbOutputs() == 2); return true; } @@ -177,7 +176,7 @@ bool SampleUffSSD::constructNetwork(SampleUniquePtr& builder SampleUniquePtr& network, SampleUniquePtr& config, SampleUniquePtr& parser) { - parser->registerInput(mParams.inputTensorNames[0].c_str(), DimsCHW(3, 300, 300), nvuffparser::UffInputOrder::kNCHW); + parser->registerInput(mParams.inputTensorNames[0].c_str(), Dims3(3, 300, 300), nvuffparser::UffInputOrder::kNCHW); parser->registerOutput(mParams.outputTensorNames[0].c_str()); auto parsed = parser->parse(locateFile(mParams.uffFileName, mParams.dataDirs).c_str(), *network, DataType::kFLOAT); @@ -203,8 +202,8 @@ bool SampleUffSSD::constructNetwork(SampleUniquePtr& builder const int32_t imageC = 3; const int32_t imageH = 300; const int32_t imageW = 300; - nvinfer1::DimsNCHW imageDims{}; - imageDims = nvinfer1::DimsNCHW{mParams.calBatchSize, imageC, imageH, imageW}; + nvinfer1::Dims4 imageDims{}; + imageDims = nvinfer1::Dims4{mParams.calBatchSize, imageC, imageH, imageW}; BatchStream calibrationStream( mParams.calBatchSize, mParams.nbCalBatches, imageDims, listFileName, mParams.dataDirs); calibrator.reset(new Int8EntropyCalibrator2( @@ -213,8 +212,28 @@ bool SampleUffSSD::constructNetwork(SampleUniquePtr& builder config->setInt8Calibrator(calibrator.get()); } + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + if (!profileStream) + { + return false; + } + config->setProfileStream(*profileStream); + + SampleUniquePtr plan{builder->buildSerializedNetwork(*network, *config)}; + if (!plan) + { + return false; + } + + SampleUniquePtr runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; + if (!runtime) + { + return false; + } + mEngine = std::shared_ptr( - builder->buildEngineWithConfig(*network, *config), samplesCommon::InferDeleter()); + runtime->deserializeCudaEngine(plan->data(), plan->size()), samplesCommon::InferDeleter()); if (!mEngine) { return false; @@ -241,7 +260,7 @@ bool SampleUffSSD::infer() } // Read the input data into the managed buffers - assert(mParams.inputTensorNames.size() == 1); + ASSERT(mParams.inputTensorNames.size() == 1); if (!processInput(buffers)) { return false; diff --git a/samples/opensource/trtexec/tracer.py b/samples/opensource/trtexec/tracer.py index 8caa1830..28ff1106 100755 --- a/samples/opensource/trtexec/tracer.py +++ b/samples/opensource/trtexec/tracer.py @@ -45,7 +45,7 @@ defaultMetrics = ",".join(allMetrics) descriptions = ['start input', 'end input', 'start compute', 'end compute', 'start output', 'end output', 'input', 'compute', 'output', 'latency', 'end to end latency'] -metricsDescription = pu.combineDescriptions('Possible metrics (all in ms) are:', +metricsDescription = pu.combine_descriptions('Possible metrics (all in ms) are:', allMetrics, descriptions) @@ -112,7 +112,7 @@ def main(): args = parser.parse_args() metrics = args.metrics.split(',') - count = args.gp and (not hasTimestamp(metricts) or len(metrics) == 1) + count = args.gp and (not hasTimestamp(metrics) or len(metrics) == 1) if not args.no_header: pu.printHeader(allMetrics, metrics, args.gp, count) diff --git a/samples/opensource/trtexec/trtexec.cpp b/samples/opensource/trtexec/trtexec.cpp index a8db426f..c9a5b890 100644 --- a/samples/opensource/trtexec/trtexec.cpp +++ b/samples/opensource/trtexec/trtexec.cpp @@ -59,7 +59,7 @@ void printPerformanceProfile(const ReportingOptions& reporting, const InferenceE } } -void printOutput(const ReportingOptions& reporting, const InferenceEnvironment& iEnv, std::ostream& os) +void printOutput(const ReportingOptions& reporting, const InferenceEnvironment& iEnv, std::ostream& os, int32_t batch) { if (reporting.output) { @@ -67,7 +67,7 @@ void printOutput(const ReportingOptions& reporting, const InferenceEnvironment& } if (!reporting.exportOutput.empty()) { - exportJSONOutput(*iEnv.context.front(), *iEnv.bindings.front(), reporting.exportOutput); + exportJSONOutput(*iEnv.context.front(), *iEnv.bindings.front(), reporting.exportOutput, batch); } } @@ -136,6 +136,7 @@ int main(int argc, char** argv) setCudaDevice(options.system.device, sample::gLogInfo); sample::gLogInfo << std::endl; + sample::gLogInfo << "TensorRT version: " << getInferLibVersion() << std::endl; initLibNvInferPlugins(&sample::gLogger.getTRTLogger(), ""); for (const auto& pluginPath : options.system.plugins) @@ -145,25 +146,57 @@ int main(int argc, char** argv) } InferenceEnvironment iEnv; - time_point buildStartTime{std::chrono::high_resolution_clock::now()}; - iEnv.engine = getEngine(options.model, options.build, options.system, sample::gLogError); - time_point buildEndTime{std::chrono::high_resolution_clock::now()}; + TrtUniquePtr networkForRefit; + Parser parserHoldingWeightsMem; + const time_point buildStartTime{std::chrono::high_resolution_clock::now()}; + std::tie(iEnv.engine, networkForRefit, parserHoldingWeightsMem) = getEngineNetworkParserTuple(options.model, options.build, options.system, sample::gLogError); + const time_point buildEndTime{std::chrono::high_resolution_clock::now()}; if (iEnv.engine) { - sample::gLogInfo << "Engine " << (options.build.load ? "loaded" : "built") << " in " - << duration(buildEndTime - buildStartTime).count() << " sec." << std::endl; + sample::gLogInfo << "Engine " << (options.build.load ? "loaded" : "built") + << " in " << duration(buildEndTime - buildStartTime).count() << " sec." << std::endl; } else { sample::gLogError << "Engine set up failed" << std::endl; return sample::gLogger.reportFail(sampleTest); } - - if (iEnv.engine.get()->isRefittable() && options.reporting.refit) + if (iEnv.engine.get()->isRefittable()) { - dumpRefittable(*iEnv.engine.get()); + if (options.reporting.refit) + { + dumpRefittable(*iEnv.engine.get()); + } + if (options.inference.timeRefit) + { + if (networkForRefit.operator bool()) + { + const bool success = timeRefit(*networkForRefit, *iEnv.engine); + if (!success) + { + sample::gLogError << "Engine refit failed." << std::endl; + return sample::gLogger.reportFail(sampleTest); + } + } + else + { + sample::gLogWarning << "Network not available, skipped timing refit." << std::endl; + } + } } + // release resources for refit only. + parserHoldingWeightsMem = Parser{}; + // network released after parser! parser destructor depends on network. + networkForRefit.reset(); + if (options.inference.timeDeserialize) + { + if (timeDeserialize(iEnv)) + { + return sample::gLogger.reportFail(sampleTest); + } + return sample::gLogger.reportPass(sampleTest); + } if (options.inference.skip) { return sample::gLogger.reportPass(sampleTest); @@ -197,11 +230,16 @@ int main(int argc, char** argv) } std::vector trace; sample::gLogInfo << "Starting inference" << std::endl; - runInference(options.inference, iEnv, options.system.device, trace); + + if (!runInference(options.inference, iEnv, options.system.device, trace)) + { + sample::gLogError << "Error occurred during inference" << std::endl; + return sample::gLogger.reportFail(sampleTest); + } printPerformanceReport(trace, options.reporting, static_cast(options.inference.warmup), - options.inference.batch, sample::gLogInfo); - printOutput(options.reporting, iEnv, sample::gLogInfo); + options.inference.batch, sample::gLogInfo, sample::gLogWarning, sample::gLogVerbose); + printOutput(options.reporting, iEnv, sample::gLogInfo, options.inference.batch); if ((options.reporting.profile || !options.reporting.exportProfile.empty()) && options.inference.rerun) { @@ -215,7 +253,11 @@ int main(int argc, char** argv) "and disabled CUDA graph in the second run with the profiler." << std::endl; } - runInference(options.inference, iEnv, options.system.device, trace); + if (!runInference(options.inference, iEnv, options.system.device, trace)) + { + sample::gLogError << "Error occurred during inference" << std::endl; + return sample::gLogger.reportFail(sampleTest); + } } printPerformanceProfile(options.reporting, iEnv, sample::gLogInfo); diff --git a/samples/python/README.md b/samples/python/README.md index 5fae7346..857a9638 100644 --- a/samples/python/README.md +++ b/samples/python/README.md @@ -1,13 +1,68 @@ -# General Setup for Python Samples +General Setup Guide for Samples +============================== -## Prerequisites -Dependencies can be istalled using: - ```bash - python3 -m pip install -r requirements.txt - ``` +## Download Sample Data -Data can be downloaded using the following utility if `download.yml` is present in the sample directory ([example](yolov3_onnx/download.yml)). - ```bash - downloader.py -d /path/to/data/dir -f /path/to/download.yml - ``` +Install the tool dependencies via `python3 -m pip install -r requirements.txt`. + +Invoke [downloader.py](downloader.py) to download the data with +a command like the one below if `download.yml` is present in the +sample directory ([example](yolov3_onnx/download.yml)). + +```sh +downloader.py -d /path/to/data/dir -f /path/to/download.yml +``` + +The data directory i.e. `/path/to/data/dir` is a centralized directory +to store data of all samples. So you can use same one for all samples. +It can be provided by either `-d /path/to/data/dir` or the environment variable +`$TRT_DATA_DIR`, where the `-d` has higher priority. + +Remember to use `-d` or `$TRT_DATA_DIR` when running sample scripts +that rely on downloaded data. Scripts will abort if no downloaded data +is found in data directory. (`$TRT_DATA_DIR` will be much simplier.) +An error will be thrown if the data is not properly setup. + +The `download.yml` file is owned by the sample which describes the sample +name, the path, URL and checksum of the data files that are required by the sample. + + +**Notes for sample developers** + +To use the downloaded data files, integrate the code segment like below into +the sample code, and obtain the path to the data file by passing the `path` +as specified in the associated `download.yml` file of the sample. +For example, to obtain path to the downloaded VOC dataset file of sample +[uff_ssd](uff_ssd), use `getFilePath('samples/python/uff_ssd/VOCtest_06-Nov-2007.tar')`. +The following example illustrates how to access the downloaded data in sample [uff_ssd](uff_ssd). + +```py +TRT_DATA_DIR = None + +def getFilePath(path): + global TRT_DATA_DIR + if not TRT_DATA_DIR: + parser = argparse.ArgumentParser(description="Convert YOLOv3 to ONNX model") + parser.add_argument('-d', '--data', help="Specify the data directory where it is saved in. $TRT_DATA_DIR will be overwritten by this argument.") + args, _ = parser.parse_known_args() + TRT_DATA_DIR = os.environ.get('TRT_DATA_DIR', None) if args.data is None else args.data + if TRT_DATA_DIR is None: + raise ValueError("Data directory must be specified by either `-d $DATA` or environment variable $TRT_DATA_DIR.") + + fullpath = os.path.join(TRT_DATA_DIR, path) + if not os.path.exists(fullpath): + raise ValueError("Data file %s doesn't exist!" % fullpath) + + return fullpath +``` + +The helper function `getFilePath` in `downloader.py` can also be used to obtain the +full path to the downloaded data files. It only works when the sample doesn't have +any other command line argument. + +```py +from downloader import getFilePath + +cfg_file_path = getFilePath('samples/python/yolov3_onnx/yolov3.cfg') +``` diff --git a/samples/python/common.py b/samples/python/common.py index d94c50d8..65ffbb37 100644 --- a/samples/python/common.py +++ b/samples/python/common.py @@ -14,14 +14,12 @@ # limitations under the License. # -from itertools import chain import argparse import os -import pycuda.driver as cuda -import pycuda.autoinit import numpy as np - +import pycuda.autoinit +import pycuda.driver as cuda import tensorrt as trt try: @@ -165,77 +163,3 @@ def do_inference_v2(context, bindings, inputs, outputs, stream): stream.synchronize() # Return only the host outputs. return [out.host for out in outputs] - -def generate_md5_checksum(local_path): - """Returns the MD5 checksum of a local file. - - Keyword argument: - local_path -- path of the file whose checksum shall be generated - """ - with open(local_path, 'rb') as local_file: - data = local_file.read() - import hashlib - return hashlib.md5(data).hexdigest() - - -def download_file(local_path, link, checksum_reference=None): - """Checks if a local file is present and downloads it from the specified path otherwise. - If checksum_reference is specified, the file's md5 checksum is compared against the - expected value. - - Keyword arguments: - local_path -- path of the file whose checksum shall be generated - link -- link where the file shall be downloaded from if it is not found locally - checksum_reference -- expected MD5 checksum of the file - """ - if not os.path.exists(local_path): - print('Downloading from %s, this may take a while...' % link) - import wget - wget.download(link, local_path) - print() - if checksum_reference is not None: - checksum = generate_md5_checksum(local_path) - if checksum != checksum_reference: - raise ValueError( - 'The MD5 checksum of local file %s differs from %s, please manually remove \ - the file and try again.' % - (local_path, checksum_reference)) - return local_path - - -# `retry_call` and `retry` are used to wrap the function we want to try multiple times -def retry_call(func, args=[], kwargs={}, n_retries=3): - """Wrap a function to retry it several times. - - Args: - func: function to call - args (List): args parsed to func - kwargs (Dict): kwargs parsed to func - n_retries (int): maximum times of tries - """ - for i_try in range(n_retries): - try: - func(*args, **kwargs) - break - except: - if i_try == n_retries - 1: - raise - print("retry...") - -# Usage: @retry(n_retries) -def retry(n_retries=3): - """Wrap a function to retry it several times. Decorator version of `retry_call`. - - Args: - n_retries (int): maximum times of tries - - Usage: - @retry(n_retries) - def func(...): - pass - """ - def wrapper(func): - def _wrapper(*args, **kwargs): - retry_call(func, args, kwargs, n_retries) - return _wrapper - return wrapper diff --git a/samples/python/downloader.py b/samples/python/downloader.py index 200914d4..97eb37bb 100755 --- a/samples/python/downloader.py +++ b/samples/python/downloader.py @@ -127,7 +127,6 @@ def _parseArgs(): action='store_true', default=False) parser.add_argument('-v', '--verify', help="Verify if the data has been downloaded. Will not download if specified.", action='store_true', default=False) - parser.add_argument('-V', '--verbose', help="Dump debug log", action='store_true', default=False) args, _ = parser.parse_known_args() data = os.environ.get('TRT_DATA_DIR', None) if args.data is None else args.data @@ -150,7 +149,7 @@ def verifyChecksum(data_dir, yaml_path): fpath = os.path.join(data_dir, f.path) if os.path.exists(fpath): if _checkMD5(fpath, f.checksum): - logger.debug("MD5 match for local copy %s", fpath) + logger.info("MD5 match for local copy %s", fpath) else: logger.error("Local file %s has a different checksum!", fpath) allGood = False @@ -163,9 +162,8 @@ def verifyChecksum(data_dir, yaml_path): def main(): data, args = _parseArgs() - if args.verbose: - logging.basicConfig() - logger.setLevel(logging.DEBUG) + logging.basicConfig() + logger.setLevel(logging.INFO) ret = True if args.verify: @@ -180,3 +178,26 @@ def main(): if __name__ == '__main__': main() + + +TRT_DATA_DIR = None + +def getFilePath(path): + """Util to get the full path to the downloaded data files. + + It only works when the sample doesn't have any other command line argument. + """ + global TRT_DATA_DIR + if not TRT_DATA_DIR: + parser = argparse.ArgumentParser(description="Helper of data file download tool") + parser.add_argument('-d', '--data', help="Specify the data directory where it is saved in. $TRT_DATA_DIR will be overwritten by this argument.") + args, _ = parser.parse_known_args() + TRT_DATA_DIR = os.environ.get('TRT_DATA_DIR', None) if args.data is None else args.data + if TRT_DATA_DIR is None: + raise ValueError("Data directory must be specified by either `-d $DATA` or environment variable $TRT_DATA_DIR.") + + fullpath = os.path.join(TRT_DATA_DIR, path) + if not os.path.exists(fullpath): + raise ValueError("Data file %s doesn't exist!" % fullpath) + + return fullpath diff --git a/samples/python/efficientdet/README.md b/samples/python/efficientdet/README.md new file mode 100644 index 00000000..5a98dd95 --- /dev/null +++ b/samples/python/efficientdet/README.md @@ -0,0 +1,284 @@ +# EfficientDet Object Detection in TensorRT + +![efficientdet](https://drive.google.com/uc?export=view&id=1Le98wETvmKKj0fUKoCFLsld7o8QPJq9C) + +These scripts help with conversion and execution of [Google EfficientDet](https://arxiv.org/abs/1911.09070) models with [NVIDIA TensorRT](https://developer.nvidia.com/tensorrt). This process is compatible with models trained through either Google AutoML or the TensorFlow Object Detection API. + +## Contents +- [Setup](#setup) +- [Model Conversion](#model-conversion) + * [TensorFlow Saved Model](#tensorflow-saved-model) + * [Create ONNX Graph](#create-onnx-graph) + * [Build TensorRT Engine](#build-tensorrt-engine) +- [Inference](#inference) + * [Inference in Python](#inference-in-python) + * [Evaluate mAP Metric](#evaluate-map-metric) + * [TF vs TRT Comparison](#tf-vs-trt-comparison) + +## Setup + +For best results, we recommend running these scripts on an environment with TensorRT >= 8.0.1 and TensorFlow 2.5. + +Install TensorRT as per the [TensorRT Install Guide](https://docs.nvidia.com/deeplearning/tensorrt/install-guide/index.html). You will need to make sure the Python bindings for TensorRT are also installed correctly, these are available by installing the `python3-libnvinfer` and `python3-libnvinfer-dev` packages on your TensorRT download. + +Install all dependencies listed in `requirements.txt`: + +``` +pip install -r requirements.txt +``` + +You will also need the latest `onnx_graphsurgeon` python module. If not already installed by TensorRT, you can install it manually by running: + +``` +pip install onnx-graphsurgeon --index-url https://pypi.ngc.nvidia.com +``` + +**NOTE:** Please make sure that the `onnx-graphsurgeon` module installed by pip is version >= 0.3.9. + +Finally, you may want to clone the EfficientDet code from the [AutoML Repository](https://github.com/google/automl) to use some helper utilities from it: + +``` +git clone https://github.com/google/automl +``` + +## Model Conversion + +The workflow to convert an EfficientDet model is basically TensorFlow → ONNX → TensorRT, and so parts of this process require TensorFlow to be installed. If you are performing this conversion to run inference on the edge, such as for NVIDIA Jetson devices, it might be easier to do the ONNX conversion on a PC first. + +### TensorFlow Saved Model + +The starting point of conversion is a TensorFlow saved model. This can be exported from your own trained models, or you can download a pre-trained model. This conversion script is compatible with two types of models: + +1. EfficientDet models trained with the [AutoML](https://github.com/google/automl/tree/master/efficientdet) framework. +2. EfficientDet models trained with the [TensorFlow Object Detection](https://github.com/tensorflow/models/tree/master/research/object_detection) API (TFOD). + +#### 1. AutoML Models + +You can download one of the pre-trained AutoML saved models from the [EfficientDet TFHub](https://tfhub.dev/s?network-architecture=efficientdet), such as: + +``` +wget https://storage.googleapis.com/tfhub-modules/tensorflow/efficientdet/d0/1.tar.gz +``` + +The contents of this package, when extracted, will hold a saved model ready for conversion. + +**NOTE:** Some saved models in TFHub may give problems with ONNX conversion. If so, please download the original checkpoint and export the saved model manually as per the instructions below. + +Alternatively, if you are training your own model, or if you need to re-export the saved model manually, you will need the training checkpoint (or a pre-trained "ckpt" from the [AutoML Repository](https://github.com/google/automl/tree/master/efficientdet) such as [this](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d0.tar.gz)). The checkpoint directory should have a file structure such as this: + +``` +efficientdet-d0 +├── model.data-00000-of-00001 +├── model.index +└── model.meta +``` + +To export a saved model from here, clone and install the [AutoML](https://github.com/google/automl) repository, and run: + +``` +cd /path/to/automl/efficientdet +python model_inspect.py \ + --runmode saved_model \ + --model_name efficientdet-d0 \ + --ckpt_path /path/to/efficientdet-d0 \ + --saved_model_dir /path/to/saved_model +``` + +Where the `--model_name` argument is the network name corresponding to this checkpoint, usually between `efficientdet-d0` and `efficientdet-d7x`. The `--ckpt_path` points to the directory holding the checkpoint as described above. The TF saved model will be exported to the path given by `--saved_model_dir`. + +> **Custom Image Size:** If your application requires inference at a different image resolution than the training input size, you can re-export the model for the exact size you require. To do so, export a saved model from checkpoint as shown above, but add an extra argument as: `--hparams 'image_size=1920x1280'` + +#### 2. TFOD Models + +You can download one of the pre-trained TFOD models from the [TF2 Detection Model Zoo](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf2_detection_zoo.md), such as: + +``` +wget http://download.tensorflow.org/models/object_detection/tf2/20200711/efficientdet_d0_coco17_tpu-32.tar.gz +``` + +When extracted, this package holds a directory named `saved_model` which holds the saved model ready for conversion. + +However, if you are working with your own trained model, or if you need to re-export the saved model, you can do so from the training checkpoint. The downloaded package above also contains a pre-trained checkpoint. The structure is similar to this: + +``` +efficientdet_d0_coco17_tpu-32 +├── checkpoint +│ ├── ckpt-0.data-00000-of-00001 +│ └── ckpt-0.index +├── pipeline.config +└── saved_model + └── saved_model.pb +``` + +To (re-)export a saved model from here, clone and install the TFOD API from the [TF Models Repository](https://github.com/tensorflow/models) repository, and run: + +``` +cd /path/to/models/research/object_detection +python exporter_main_v2.py \ + --input_type image_tensor \ + --trained_checkpoint_dir /path/to/efficientdet_d0_coco17_tpu-32/checkpoint \ + --pipeline_config_path /path/to/efficientdet_d0_coco17_tpu-32/pipeline.config \ + --output_directory /path/to/export +``` + +Where `--trained_checkpoint_dir` and `--pipeline_config_path` point to the corresponding paths in the training checkpoint. On the path pointed by `--output_directory` you will then find the newly created saved model in a directory aptly named `saved_model`. + +**NOTE:** TFOD EfficientDet models will have a slightly reduced throughput than their AutoML model counterparts. This is due to differences in the graph construction that TFOD makes use of. + +### Create ONNX Graph + +To generate an ONNX model file, first find the input shape that corresponds to the model you're converting: + +| **Model** | **Input Shape** | +| -----------------|-----------------| +| EfficientDet D0 | N,512,512,3 | +| EfficientDet D1 | N,640,640,3 | +| EfficientDet D2 | N,768,768,3 | +| EfficientDet D3 | N,896,896,3 | +| EfficientDet D4 | N,1024,1024,3 | +| EfficientDet D5 | N,1280,1280,3 | +| EfficientDet D6 | N,1280,1280,3 | +| EfficientDet D7 | N,1536,1536,3 | +| EfficientDet D7x | N,1536,1536,3 | + +Where **N** is the batch size you would like to run inference at, such as `8,512,512,3` for a batch size of 8. If you exported the saved model with a custom input image size, you should use that specific shape instead. + +The ONNX conversion process supports both `NHWC` and `NCHW` input formats, so if your input source is an `NCHW` data format, you can use the corresponding input shape, i.e. `1,512,512,3` -> `1,3,512,512`. + +With the correct input shape selected, and the TF saved model ready to be converted, run: + +``` +python create_onnx.py \ + --input_shape '1,512,512,3' \ + --saved_model /path/to/saved_model \ + --onnx /path/to/model.onnx +``` + +This will create the file `model.onnx` which is ready to convert to TensorRT. + +The script has a few optional arguments, including: + +* `--nms_threshold [...]` allows overriding the default NMS score threshold parameter, as the runtime latency of the NMS plugin is sensitive to this value. It's a good practice to set this value as high as possible, while still fulfilling your application requirements, to reduce inference latency. +* `--legacy_plugins` allows falling back to older plugins on systems where a version lower than TensorRT 8.0.1 is installed. This will result in substantially slower inference times however, but is provided for compatibility. + +Optionally, you may wish to visualize the resulting ONNX graph with a tool such as [Netron](https://netron.app/). + +![netron](https://drive.google.com/uc?export=view&id=1m9zRbvNtlbftN7P46dtOLPbcwEbz4XwS) + +The input to the graph is a `float32` tensor with the selected input shape, containing RGB pixel data in the range of 0 to 255. Normalization, mean subtraction and scaling will be performed inside the EfficientDet graph, so it is not required to further pre-process the input data. + +The outputs of the graph are the same as the outputs of the [EfficientNMS](https://github.com/NVIDIA/TensorRT/tree/master/plugin/efficientNMSPlugin) plugin. If the ONNX graph was created with `--legacy_plugins` for TensorRT 7 compatibility, the outputs will correspond to those of the [BatchedNMS](https://github.com/NVIDIA/TensorRT/tree/master/plugin/batchedNMSPlugin) plugin instead. + +### Build TensorRT Engine + +It is possible to build the TensorRT engine directly with `trtexec` using the ONNX graph generated in the previous step. However, the script `build_engine.py` is provided for convenience, as it has been tailored to EfficientDet engine building and calibration. Run `python build_engine.py --help` for details on available settings. + +#### FP16 Precision + +To build the TensorRT engine file with FP16 precision, run: + +``` +python build_engine.py \ + --onnx /path/to/model.onnx \ + --engine /path/to/engine.trt \ + --precision fp16 +``` + +The file `engine.trt` will be created, which can now be used to infer with TensorRT. + +For best results, make sure no other processes are using the GPU during engine build, as it may affect the optimal tactic selection process. + +#### INT8 Precision + +To build and calibrate an engine for INT8 precision, run: + +``` +python build_engine.py \ + --onnx /path/to/model.onnx \ + --engine /path/to/engine.trt \ + --precision int8 \ + --calib_input /path/to/calibration/images \ + --calib_cache /path/to/calibration.cache +``` + +Where `--calib_input` points to a directory with several thousands of images. For example, this could be a subset of the training or validation datasets that were used for the model. It's important that this data represents the runtime data distribution relatively well, therefore, the more images that are used for calibration, the better accuracy that will be achieved in INT8 precision. For models trained for the [COCO dataset](https://cocodataset.org/#home), we have found that 5,000 images gives a good result. + +The `--calib_cache` controls where the calibration cache file will be written to. This is useful to keep a cached copy of the calibration results. Next time you need to build the engine for the same network, if this file exists, it will skip the calibration step and use the cached values instead. + +#### Benchmark Engine + +Optionally, you can obtain execution timing information for the built engine by using the `trtexec` utility, as: + +``` +trtexec \ + --loadEngine=/path/to/engine.trt \ + --useCudaGraph --noDataTransfers \ + --iterations=100 --avgRuns=100 +``` + +If it's not already in your `$PATH`, the `trtexec` binary is usually found in `/usr/src/tensorrt/bin/trtexec`, depending on your TensorRT installation method. + +An inference benchmark will run, with GPU Compute latency times printed out to the console. Depending on your environment, you should see something similar to: + +``` +GPU Compute Time: min = 1.55835 ms, max = 1.91591 ms, mean = 1.58719 ms, median = 1.578 ms, percentile(99%) = 1.90668 ms +``` + +## Inference + +For optimal performance, inference should be done in a C++ application that takes advantage of CUDA Graphs to launch the inference request. Alternatively, the TensorRT engine built with this process can also be executed through either [Triton Inference Server](https://developer.nvidia.com/nvidia-triton-inference-server) or [DeepStream SDK](https://developer.nvidia.com/deepstream-sdk). + +However, for convenience, a python inference script is provided here for quick testing of the built TensorRT engine. + +### Inference in Python + +To perform object detection on a set of images with TensorRT, run: + +``` +python infer.py \ + --engine /paht/to/engine.trt \ + --input /path/to/images \ + --output /path/to/output +``` + +Where the input path can be either a single image file, or a directory of jpg/png/bmp images. + +The detection results will be written out to the specified output directory, consisting of a visualization image, and a tab-separated results file for each input image processed. + +![infer](https://drive.google.com/uc?export=view&id=1ZzTHizLx65t_cJcIIflnzXA5yxCYsQz6) + +> *This example is generated with a TensorRT engine for the pre-trained AutoML EfficientDet-D0 model re-exported with a custom image size of 1920x1080 as described above. The engine uses an NMS score threshold of 0.4. This is the same [sample image](https://user-images.githubusercontent.com/11736571/77320690-099af300-6d37-11ea-9d86-24f14dc2d540.png) and model parameters as used in the AutoML [inference tutorial](https://github.com/google/automl/blob/master/efficientdet/g3doc/street.jpg).* + +### Evaluate mAP Metric + +Given a validation dataset (such as [COCO val2017 data](http://images.cocodataset.org/zips/val2017.zip)) and ground truth annotations (such as [COCO instances_val2017.json](http://images.cocodataset.org/annotations/annotations_trainval2017.zip)), you can get the mAP metrics for the built TensorRT engine. This will use the mAP metrics calculation script from the [AutoML](https://github.com/google/automl) repository. + +``` +python eval_coco.py \ + --engine /path/to/engine.trt \ + --input /path/to/coco/val2017 \ + --annotations /path/to/coco/annotations/instances_val2017.json \ + --automl_path /path/to/automl +``` + +Where the `--automl_path` argument points to the root of the AutoML repository. + +The mAP metric is sensitive to the NMS score threshold used, as using a high threshold will reduce the model recall, resulting in a lower mAP value. Ideally, mAP should be measured with a threshold of 0, but such a low value will impact the runtime latency of the EfficientNMS plugin. It may be a good idea to build separate TensorRT engines for different purposes. That is, one engine with a low threshold (like 0) dedicated for mAP validation, and another engine with your application specific threshold (like 0.4) for deployment. This is why we keep the NMS threshold as a configurable parameter in the `create_onnx.py` script. + +### TF vs TRT Comparison + +To compare how the TensorRT detections match the original TensorFlow model results, you can run: + +``` +python compare_tf.py \ + --engine /path/to/engine.trt \ + --saved_model /path/to/saved_model \ + --input /path/to/images \ + --output /path/to/output +``` + +This script will process the images found in the given input path through both TensorFlow and TensorRT using the corresponding saved model and engine. It will then write to the output path a set of visualization images showing the inference results of both frameworks for visual qualitative comparison. + +If you run this on COCO val2017 images, you may also add the parameter `--annotations /path/to/coco/annotations/instances_val2017.json` to further compare against COCO ground truth annotations. + +![compare_tf](https://drive.google.com/uc?export=view&id=1zgh_RbYX6RWzu7nKLCcSzy60VPiQROZJ) \ No newline at end of file diff --git a/samples/python/efficientdet/build_engine.py b/samples/python/efficientdet/build_engine.py new file mode 100644 index 00000000..dfae3588 --- /dev/null +++ b/samples/python/efficientdet/build_engine.py @@ -0,0 +1,240 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 os +import sys +import logging +import argparse + +import numpy as np +import tensorrt as trt +import pycuda.driver as cuda +import pycuda.autoinit + +from image_batcher import ImageBatcher + +logging.basicConfig(level=logging.INFO) +logging.getLogger("EngineBuilder").setLevel(logging.INFO) +log = logging.getLogger("EngineBuilder") + + +class EngineCalibrator(trt.IInt8EntropyCalibrator2): + """ + Implements the INT8 Entropy Calibrator 2. + """ + + def __init__(self, cache_file): + """ + :param cache_file: The location of the cache file. + """ + super().__init__() + self.cache_file = cache_file + self.image_batcher = None + self.batch_allocation = None + self.batch_generator = None + + def set_image_batcher(self, image_batcher: ImageBatcher): + """ + Define the image batcher to use, if any. If using only the cache file, an image batcher doesn't need + to be defined. + :param image_batcher: The ImageBatcher object + """ + self.image_batcher = image_batcher + size = int(np.dtype(self.image_batcher.dtype).itemsize * np.prod(self.image_batcher.shape)) + self.batch_allocation = cuda.mem_alloc(size) + self.batch_generator = self.image_batcher.get_batch() + + def get_batch_size(self): + """ + Overrides from trt.IInt8EntropyCalibrator2. + Get the batch size to use for calibration. + :return: Batch size. + """ + if self.image_batcher: + return self.image_batcher.batch_size + return 1 + + def get_batch(self, names): + """ + Overrides from trt.IInt8EntropyCalibrator2. + Get the next batch to use for calibration, as a list of device memory pointers. + :param names: The names of the inputs, if useful to define the order of inputs. + :return: A list of int-casted memory pointers. + """ + if not self.image_batcher: + return None + try: + batch, _, _ = next(self.batch_generator) + log.info("Calibrating image {} / {}".format(self.image_batcher.image_index, self.image_batcher.num_images)) + cuda.memcpy_htod(self.batch_allocation, np.ascontiguousarray(batch)) + return [int(self.batch_allocation)] + except StopIteration: + log.info("Finished calibration batches") + return None + + def read_calibration_cache(self): + """ + Overrides from trt.IInt8EntropyCalibrator2. + Read the calibration cache file stored on disk, if it exists. + :return: The contents of the cache file, if any. + """ + if os.path.exists(self.cache_file): + with open(self.cache_file, "rb") as f: + log.info("Using calibration cache file: {}".format(self.cache_file)) + return f.read() + + def write_calibration_cache(self, cache): + """ + Overrides from trt.IInt8EntropyCalibrator2. + Store the calibration cache to a file on disk. + :param cache: The contents of the calibration cache to store. + """ + with open(self.cache_file, "wb") as f: + log.info("Writing calibration cache data to: {}".format(self.cache_file)) + f.write(cache) + + +class EngineBuilder: + """ + Parses an ONNX graph and builds a TensorRT engine from it. + """ + + def __init__(self, verbose=False, workspace=8): + """ + :param verbose: If enabled, a higher verbosity level will be set on the TensorRT logger. + :param workspace: Max memory workspace to allow, in Gb. + """ + self.trt_logger = trt.Logger(trt.Logger.INFO) + if verbose: + self.trt_logger.min_severity = trt.Logger.Severity.VERBOSE + + trt.init_libnvinfer_plugins(self.trt_logger, namespace="") + + self.builder = trt.Builder(self.trt_logger) + self.config = self.builder.create_builder_config() + self.config.max_workspace_size = workspace * (2 ** 30) + + self.batch_size = None + self.network = None + self.parser = None + + def create_network(self, onnx_path): + """ + Parse the ONNX graph and create the corresponding TensorRT network definition. + :param onnx_path: The path to the ONNX graph to load. + """ + network_flags = (1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)) + + self.network = self.builder.create_network(network_flags) + self.parser = trt.OnnxParser(self.network, self.trt_logger) + + onnx_path = os.path.realpath(onnx_path) + with open(onnx_path, "rb") as f: + if not self.parser.parse(f.read()): + log.error("Failed to load ONNX file: {}".format(onnx_path)) + for error in range(self.parser.num_errors): + log.error(self.parser.get_error(error)) + sys.exit(1) + + inputs = [self.network.get_input(i) for i in range(self.network.num_inputs)] + outputs = [self.network.get_output(i) for i in range(self.network.num_outputs)] + + log.info("Network Description") + for input in inputs: + self.batch_size = input.shape[0] + log.info("Input '{}' with shape {} and dtype {}".format(input.name, input.shape, input.dtype)) + for output in outputs: + log.info("Output '{}' with shape {} and dtype {}".format(output.name, output.shape, output.dtype)) + assert self.batch_size > 0 + self.builder.max_batch_size = self.batch_size + + def create_engine(self, engine_path, precision, calib_input=None, calib_cache=None, calib_num_images=5000, + calib_batch_size=8): + """ + Build the TensorRT engine and serialize it to disk. + :param engine_path: The path where to serialize the engine to. + :param precision: The datatype to use for the engine, either 'fp32', 'fp16' or 'int8'. + :param calib_input: The path to a directory holding the calibration images. + :param calib_cache: The path where to write the calibration cache to, or if it already exists, load it from. + :param calib_num_images: The maximum number of images to use for calibration. + :param calib_batch_size: The batch size to use for the calibration process. + """ + engine_path = os.path.realpath(engine_path) + engine_dir = os.path.dirname(engine_path) + os.makedirs(engine_dir, exist_ok=True) + log.info("Building {} Engine in {}".format(precision, engine_path)) + + inputs = [self.network.get_input(i) for i in range(self.network.num_inputs)] + + if precision == "fp16": + if not self.builder.platform_has_fast_fp16: + log.warning("FP16 is not supported natively on this platform/device") + else: + self.config.set_flag(trt.BuilderFlag.FP16) + elif precision == "int8": + if not self.builder.platform_has_fast_int8: + log.warning("INT8 is not supported natively on this platform/device") + else: + if self.builder.platform_has_fast_fp16: + # Also enable fp16, as some layers may be even more efficient in fp16 than int8 + self.config.set_flag(trt.BuilderFlag.FP16) + self.config.set_flag(trt.BuilderFlag.INT8) + self.config.int8_calibrator = EngineCalibrator(calib_cache) + if not os.path.exists(calib_cache): + calib_shape = [calib_batch_size] + list(inputs[0].shape[1:]) + calib_dtype = trt.nptype(inputs[0].dtype) + self.config.int8_calibrator.set_image_batcher( + ImageBatcher(calib_input, calib_shape, calib_dtype, max_num_images=calib_num_images, + exact_batches=True)) + + with self.builder.build_engine(self.network, self.config) as engine, open(engine_path, "wb") as f: + log.info("Serializing engine to file: {:}".format(engine_path)) + f.write(engine.serialize()) + + +def main(args): + builder = EngineBuilder(args.verbose, args.workspace) + builder.create_network(args.onnx) + builder.create_engine(args.engine, args.precision, args.calib_input, args.calib_cache, args.calib_num_images, + args.calib_batch_size) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-o", "--onnx", help="The input ONNX model file to load") + parser.add_argument("-e", "--engine", help="The output path for the TRT engine") + parser.add_argument("-p", "--precision", default="fp16", choices=["fp32", "fp16", "int8"], + help="The precision mode to build in, either 'fp32', 'fp16' or 'int8', default: 'fp16'") + parser.add_argument("-v", "--verbose", action="store_true", help="Enable more verbose log output") + parser.add_argument("-w", "--workspace", default=8, type=int, help="The max memory workspace size to allow in Gb, " + "default: 8") + parser.add_argument("--calib_input", help="The directory holding images to use for calibration") + parser.add_argument("--calib_cache", default="./calibration.cache", + help="The file path for INT8 calibration cache to use, default: ./calibration.cache") + parser.add_argument("--calib_num_images", default=5000, type=int, + help="The maximum number of images to use for calibration, default: 5000") + parser.add_argument("--calib_batch_size", default=8, type=int, + help="The batch size for the calibration process, default: 8") + args = parser.parse_args() + if not all([args.onnx, args.engine]): + parser.print_help() + log.error("These arguments are required: --onnx and --engine") + sys.exit(1) + if args.precision == "int8" and not (args.calib_input or os.path.exists(args.calib_cache)): + parser.print_help() + log.error("When building in int8 precision, --calib_input or an existing --calib_cache file is required") + sys.exit(1) + main(args) diff --git a/samples/python/efficientdet/compare_tf.py b/samples/python/efficientdet/compare_tf.py new file mode 100644 index 00000000..26c65c4e --- /dev/null +++ b/samples/python/efficientdet/compare_tf.py @@ -0,0 +1,227 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 os +import sys +import json +import argparse + +import numpy as np +import tensorflow as tf + +from infer import TensorRTInfer +from image_batcher import ImageBatcher +from visualize import visualize_detections, concat_visualizations + + +class TensorFlowInfer: + """ + Implements TensorFlow inference of a saved model, following the same API as the TensorRTInfer class. + """ + + def __init__(self, saved_model_path): + gpus = tf.config.experimental.list_physical_devices('GPU') + for gpu in gpus: + tf.config.experimental.set_memory_growth(gpu, True) + + self.model = tf.saved_model.load(saved_model_path) + self.pred_fn = self.model.signatures['serving_default'] + + # Setup I/O bindings + self.inputs = [] + fn_inputs = self.pred_fn.structured_input_signature[1] + for i, input in enumerate(list(fn_inputs.values())): + self.inputs.append({ + 'index': i, + 'name': input.name, + 'dtype': np.dtype(input.dtype.as_numpy_dtype()), + 'shape': [1, 512, 512, 3], # This can be overridden later + }) + self.outputs = [] + fn_outputs = self.pred_fn.structured_outputs + for i, output in enumerate(list(fn_outputs.values())): + self.outputs.append({ + 'index': i, + 'name': output.name, + 'dtype': np.dtype(output.dtype.as_numpy_dtype()), + 'shape': output.shape.as_list(), + }) + + def override_input_shape(self, input, shape): + self.inputs[input]['shape'] = shape + + def input_spec(self): + return self.inputs[0]['shape'], self.inputs[0]['dtype'] + + def output_spec(self): + return self.outputs[0]['shape'], self.outputs[0]['dtype'] + + def infer(self, batch, scales=None, nms_threshold=None): + # Process I/O and execute the network + input = {self.inputs[0]['name']: tf.convert_to_tensor(batch)} + output = self.pred_fn(**input) + + # Extract the results depending on what kind of saved model this is + boxes = None + scores = None + classes = None + if len(self.outputs) == 1: + # Detected as AutoML Saved Model + assert len(self.outputs[0]['shape']) == 3 and self.outputs[0]['shape'][2] == 7 + results = output[self.outputs[0]['name']].numpy() + boxes = results[:, :, 1:5] + scores = results[:, :, 5] + classes = results[:, :, 6].astype(np.int32) + elif len(self.outputs) >= 4: + # Detected as TFOD Saved Model + assert output['num_detections'] + num = int(output['num_detections'].numpy().flatten()[0]) + boxes = output['detection_boxes'].numpy()[:, 0:num, :] + scores = output['detection_scores'].numpy()[:, 0:num] + classes = output['detection_classes'].numpy()[:, 0:num] + + # Process the results + detections = [[]] + normalized = (np.max(boxes) < 2.0) + for n in range(scores.shape[1]): + if scores[0][n] == 0.0: + break + scale = self.inputs[0]['shape'][2] if normalized else 1.0 + if scales: + scale /= scales[0] + if nms_threshold and scores[0][n] < nms_threshold: + continue + detections[0].append({ + 'ymin': boxes[0][n][0] * scale, + 'xmin': boxes[0][n][1] * scale, + 'ymax': boxes[0][n][2] * scale, + 'xmax': boxes[0][n][3] * scale, + 'score': scores[0][n], + 'class': int(classes[0][n]) - 1, + }) + return detections + + +def run(batcher, inferer, framework, nms_threshold=None): + res_images = [] + res_detections = [] + for batch, images, scales in batcher.get_batch(): + res_detections += inferer.infer(batch, scales, nms_threshold) + res_images += images + print("Processing {} / {} images ({})".format(batcher.image_index, batcher.num_images, framework), end="\r") + print() + return res_images, res_detections + + +def parse_annotations(annotations_path): + annotations = {} + if annotations_path and os.path.exists(annotations_path): + with open(annotations_path) as f: + ann_json = json.load(f) + for ann in ann_json['annotations']: + img_id = ann['image_id'] + if img_id not in annotations.keys(): + annotations[img_id] = [] + annotations[img_id].append({ + 'ymin': ann['bbox'][1], + 'xmin': ann['bbox'][0], + 'ymax': ann['bbox'][1] + ann['bbox'][3], + 'xmax': ann['bbox'][0] + ann['bbox'][2], + 'score': -1, + 'class': ann['category_id'] - 1, + }) + return annotations + + +def compare_images(tf_images, tf_detections, trt_images, trt_detections, output_dir, annotations_path, labels_path): + labels = [] + if labels_path and os.path.exists(labels_path): + with open(labels_path) as f: + for i, label in enumerate(f): + labels.append(label.strip()) + + annotations = parse_annotations(annotations_path) + + count = 1 + for tf_img, tf_det, trt_img, trt_det in zip(tf_images, tf_detections, trt_images, trt_detections): + vis = [] + names = [] + colors = [] + + vis.append(visualize_detections(tf_img, None, tf_det, labels)) + names.append("TensorFlow") + colors.append("DarkOrange") + + vis.append(visualize_detections(trt_img, None, trt_det, labels)) + names.append("TensorRT") + colors.append("YellowGreen") + + if annotations: + img_id = os.path.splitext(os.path.basename(trt_img))[0] + if img_id.isnumeric(): + img_id = int(img_id) + if img_id in annotations.keys(): + vis.append(visualize_detections(trt_img, None, annotations[img_id], labels)) + names.append("Ground Truth") + colors.append("RoyalBlue") + else: + print("Image {} does not have a COCO annotation, skipping ground truth visualization".format(trt_img)) + + basename = os.path.splitext(os.path.basename(tf_img))[0] + output_path = os.path.join(output_dir, "{}.compare.png".format(basename)) + os.makedirs(output_dir, exist_ok=True) + concat_visualizations(vis, names, colors, output_path) + + print("Processing {} / {} images (Visualization)".format(count, len(tf_images)), end="\r") + count += 1 + print() + + +def main(args): + tf_infer = TensorFlowInfer(args.saved_model) + trt_infer = TensorRTInfer(args.engine) + + trt_batcher = ImageBatcher(args.input, *trt_infer.input_spec(), max_num_images=args.num_images) + tf_infer.override_input_shape(0, [1, trt_batcher.height, trt_batcher.width, 3]) # Same size input in TF as TRT + tf_batcher = ImageBatcher(args.input, *tf_infer.input_spec(), max_num_images=args.num_images) + + tf_images, tf_detections = run(tf_batcher, tf_infer, "TensorFlow", args.nms_threshold) + trt_images, trt_detections = run(trt_batcher, trt_infer, "TensorRT", args.nms_threshold) + + compare_images(tf_images, tf_detections, trt_images, trt_detections, args.output, args.annotations, args.labels) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-e", "--engine", help="The TensorRT engine to infer with") + parser.add_argument("-m", "--saved_model", help="The TensorFlow saved model path to validate against") + parser.add_argument("-i", "--input", + help="The input to infer, either a single image path, or a directory of images") + parser.add_argument("-o", "--output", default=None, help="Directory where to save the visualization results") + parser.add_argument("-l", "--labels", default="./labels_coco.txt", + help="File to use for reading the class labels from, default: ./labels_coco.txt") + parser.add_argument("-a", "--annotations", default=None, + help="Set the path to the 'instances_val2017.json' file to use for COCO annotations, in which " + "case --input should point to the COCO val2017 dataset, default: not used") + parser.add_argument("-n", "--num_images", default=100, type=int, + help="The maximum number of images to visualize, default: 100") + parser.add_argument("-t", "--nms_threshold", type=float, help="Override the score threshold for the NMS operation, " + "if higher than the threshold in the model/engine.") + args = parser.parse_args() + if not all([args.engine, args.saved_model, args.input, args.output]): + parser.print_help() + sys.exit(1) + main(args) diff --git a/samples/python/efficientdet/create_onnx.py b/samples/python/efficientdet/create_onnx.py new file mode 100644 index 00000000..8f2beb87 --- /dev/null +++ b/samples/python/efficientdet/create_onnx.py @@ -0,0 +1,451 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 os +import sys +import argparse +import logging + +import tensorflow as tf +import onnx_graphsurgeon as gs +import numpy as np +import onnx +from onnx import shape_inference +from tf2onnx import tfonnx, optimizer, tf_loader + +import onnx_utils + +logging.basicConfig(level=logging.INFO) +logging.getLogger("EfficientDetGraphSurgeon").setLevel(logging.INFO) +log = logging.getLogger("EfficientDetGraphSurgeon") + + +class EfficientDetGraphSurgeon: + def __init__(self, saved_model_path, legacy_plugins=False): + """ + Constructor of the EfficientDet Graph Surgeon object, to do the conversion of an EfficientDet TF saved model + to an ONNX-TensorRT parsable model. + :param saved_model_path: The path pointing to the TensorFlow saved model to load. + :param legacy_plugins: If using TensorRT version < 8.0.1, set this to True to use older (but slower) plugins. + """ + saved_model_path = os.path.realpath(saved_model_path) + assert os.path.exists(saved_model_path) + + # Use tf2onnx to convert saved model to an initial ONNX graph. + graph_def, inputs, outputs = tf_loader.from_saved_model(saved_model_path, None, None, "serve", + ["serving_default"]) + log.info("Loaded saved model from {}".format(saved_model_path)) + with tf.Graph().as_default() as tf_graph: + tf.import_graph_def(graph_def, name="") + with tf_loader.tf_session(graph=tf_graph): + onnx_graph = tfonnx.process_tf_graph(tf_graph, input_names=inputs, output_names=outputs, opset=11) + onnx_model = optimizer.optimize_graph(onnx_graph).make_model("Converted from {}".format(saved_model_path)) + self.graph = gs.import_onnx(onnx_model) + assert self.graph + log.info("TF2ONNX graph created successfully") + + # Fold constants via ONNX-GS that TF2ONNX may have missed + self.graph.fold_constants() + + # Try to auto-detect by finding if nodes match a specific name pattern expected for either of the APIs. + self.api = None + if len([node for node in self.graph.nodes if "class_net/" in node.name]) > 0: + self.api = "AutoML" + elif len([node for node in self.graph.nodes if "/WeightSharedConvolutionalClassHead/" in node.name]) > 0: + self.api = "TFOD" + assert self.api + log.info("Graph was detected as {}".format(self.api)) + + self.batch_size = None + self.legacy_plugins = legacy_plugins + + def infer(self): + """ + Sanitize the graph by cleaning any unconnected nodes, do a topological resort, and fold constant inputs values. + When possible, run shape inference on the ONNX graph to determine tensor shapes. + """ + for i in range(3): + count_before = len(self.graph.nodes) + + self.graph.cleanup().toposort() + try: + for node in self.graph.nodes: + for o in node.outputs: + o.shape = None + model = gs.export_onnx(self.graph) + model = shape_inference.infer_shapes(model) + self.graph = gs.import_onnx(model) + except Exception as e: + log.info("Shape inference could not be performed at this time:\n{}".format(e)) + try: + self.graph.fold_constants(fold_shapes=True) + except TypeError as e: + log.error("This version of ONNX GraphSurgeon does not support folding shapes, please upgrade your " + "onnx_graphsurgeon module. Error:\n{}".format(e)) + raise + + count_after = len(self.graph.nodes) + if count_before == count_after: + # No new folding occurred in this iteration, so we can stop for now. + break + + def save(self, output_path): + """ + Save the ONNX model to the given location. + :param output_path: Path pointing to the location where to write out the updated ONNX model. + """ + self.graph.cleanup().toposort() + model = gs.export_onnx(self.graph) + output_path = os.path.realpath(output_path) + os.makedirs(os.path.dirname(output_path), exist_ok=True) + onnx.save(model, output_path) + log.info("Saved ONNX model to {}".format(output_path)) + + def update_preprocessor(self, input_shape): + """ + Remove all the pre-processing nodes in the ONNX graph and leave only the image normalization essentials. + :param input_shape: The input tensor shape to use for the ONNX graph. + """ + # Update the input and output tensors shape + input_shape = input_shape.split(",") + assert len(input_shape) == 4 + for i in range(len(input_shape)): + input_shape[i] = int(input_shape[i]) + assert input_shape[i] >= 1 + input_format = None + if input_shape[1] == 3: + input_format = "NCHW" + if input_shape[3] == 3: + input_format = "NHWC" + assert input_format in ["NCHW", "NHWC"] + self.batch_size = input_shape[0] + self.graph.inputs[0].shape = input_shape + self.graph.inputs[0].dtype = np.float32 + if self.api == "TFOD" and self.batch_size > 1 and self.legacy_plugins: + log.error("TFOD models with a batch size larger than 1 are not currently supported in legacy plugin mode. " + "Please upgrade to TensorRT >= 8.0.1 or use batch size 1 for now.") + sys.exit(1) + self.infer() + log.info("ONNX graph input shape: {} [{} format detected]".format(self.graph.inputs[0].shape, input_format)) + + # Find the initial nodes of the graph, whatever the input is first connected to, and disconnect them + for node in [node for node in self.graph.nodes if self.graph.inputs[0] in node.inputs]: + node.inputs.clear() + + # Convert to NCHW format if needed + input_tensor = self.graph.inputs[0] + if input_format == "NHWC": + input_tensor = self.graph.transpose("preprocessor/transpose", input_tensor, [0, 3, 1, 2]) + + # RGB Normalizers. The per-channel values are given with shape [1, 3, 1, 1] for proper NCHW shape broadcasting + scale_val = 1 / np.asarray([255], dtype=np.float32) + mean_val = -1 * np.expand_dims(np.asarray([0.485, 0.456, 0.406], dtype=np.float32), axis=(0, 2, 3)) + stddev_val = 1 / np.expand_dims(np.asarray([0.229, 0.224, 0.225], dtype=np.float32), axis=(0, 2, 3)) + # y = (x * scale + mean) * stddev --> y = x * scale * stddev + mean * stddev + scale_out = self.graph.elt_const("Mul", "preprocessor/scale", input_tensor, scale_val * stddev_val) + mean_out = self.graph.elt_const("Add", "preprocessor/mean", scale_out, mean_val * stddev_val) + + # Find the first stem conv node of the graph, and connect the normalizer directly to it + stem_name = None + if self.api == "AutoML": + stem_name = "/stem/" + if self.api == "TFOD": + stem_name = "/stem_conv2d/" + stem = [node for node in self.graph.nodes if node.op == "Conv" and stem_name in node.name][0] + log.info("Found {} node '{}' as stem entry".format(stem.op, stem.name)) + stem.inputs[0] = mean_out[0] + + # Reshape nodes tend to update the batch dimension to a fixed value of 1, they should use the batch size instead + for node in [node for node in self.graph.nodes if node.op == "Reshape"]: + if type(node.inputs[1]) == gs.Constant and node.inputs[1].values[0] == 1: + node.inputs[1].values[0] = self.batch_size + + self.infer() + + def update_network(self): + """ + Updates the graph to replace certain nodes in the main EfficientDet network: + - the global average pooling nodes are optimized when running for TFOD models. + - the nearest neighbor resize ops in the FPN are replaced by a TRT plugin nodes when running in legacy mode. + """ + + if self.api == "TFOD": + for reduce in [node for node in self.graph.nodes if node.op == "ReduceMean"]: + # TFOD models have their ReduceMean nodes applied with some redundant transposes that can be + # optimized away for better performance + # Make sure the correct subgraph is being replaced, basically search for this: + # X > Transpose (0,2,3,1) > ReduceMean (1,2) > Reshape (?,1,1,?) > Reshape (?,?,1,1) > Conv > Y + # And change to this: + # X > ReduceMean (2,3) > Conv > Y + transpose = reduce.i() + if transpose.op != "Transpose" or transpose.attrs['perm'] != [0, 2, 3, 1]: + continue + if len(reduce.attrs['axes']) != 2 or reduce.attrs['axes'] != [1, 2]: + continue + reshape1 = reduce.o() + if reshape1.op != "Reshape" or len(reshape1.inputs[1].values) != 4: + continue + if reshape1.inputs[1].values[1] != 1 or reshape1.inputs[1].values[2] != 1: + continue + reshape2 = reshape1.o() + if reshape2.op != "Reshape" or len(reshape2.inputs[1].values) != 4: + continue + if reshape2.inputs[1].values[2] != 1 or reshape2.inputs[1].values[3] != 1: + continue + conv = reshape2.o() + if conv.op != "Conv": + continue + # If all the checks above pass, then this node sequence can be optimized by just the ReduceMean itself + # operating on a different set of axes + input_tensor = transpose.inputs[0] # Input tensor to the Transpose + reduce.inputs[0] = input_tensor # Forward the Transpose input to the ReduceMean node + output_tensor = reduce.outputs[0] # Output tensor of the ReduceMean + conv.inputs[0] = output_tensor # Forward the ReduceMean output to the Conv node + reduce.attrs['axes'] = [2, 3] # Update the axes that ReduceMean operates on + reduce.attrs['keepdims'] = 1 # Keep the reduced dimensions + log.info("Optimized subgraph around ReduceMean node '{}'".format(reduce.name)) + + if self.legacy_plugins: + self.infer() + count = 1 + for node in [node for node in self.graph.nodes if node.op == "Resize" and node.attrs['mode'] == "nearest"]: + # Older versions of TensorRT do not understand nearest neighbor resize ops, so a plugin is used to + # perform this operation. + self.graph.plugin( + op="ResizeNearest_TRT", + name="resize_nearest_{}".format(count), + inputs=[node.inputs[0]], + outputs=node.outputs, + attrs={ + 'plugin_version': "1", + 'scale': 2.0, # All resize ops in the EfficientDet FPN should have an upscale factor of 2.0 + }) + node.outputs.clear() + log.info( + "Replaced '{}' ({}) with a ResizeNearest_TRT plugin node".format(node.name, count)) + count += 1 + + def update_nms(self, threshold=None, detections=None): + """ + Updates the graph to replace the NMS op by BatchedNMS_TRT TensorRT plugin node. + :param threshold: Override the score threshold attribute. If set to None, use the value in the graph. + :param detections: Override the max detections attribute. If set to None, use the value in the graph. + """ + + def find_head_concat(name_scope): + # This will find the concatenation node at the end of either Class Net or Box Net. These concatenation nodes + # bring together prediction data for each of 5 scales. + # The concatenated Class Net node will have shape [batch_size, num_anchors, num_classes], + # and the concatenated Box Net node has the shape [batch_size, num_anchors, 4]. + # These concatenation nodes can be be found by searching for all Concat's and checking if the node two + # steps above in the graph has a name that begins with either "box_net/..." or "class_net/...". + for node in [node for node in self.graph.nodes if node.op == "Transpose" and name_scope in node.name]: + concat = self.graph.find_descendant_by_op(node, "Concat") + assert concat and len(concat.inputs) == 5 + log.info("Found {} node '{}' as the tip of {}".format(concat.op, concat.name, name_scope)) + return concat + + def extract_anchors_tensor(split): + # This will find the anchors that have been hardcoded somewhere within the ONNX graph. + # The function will return a gs.Constant that can be directly used as an input to the NMS plugin. + # The anchor tensor shape will be [1, num_anchors, 4]. Note that '1' is kept as first dim, regardless of + # batch size, as it's not necessary to replicate the anchors for all images in the batch. + + # The anchors are available (one per coordinate) hardcoded as constants within certain box decoder nodes. + # Each of these four constants have shape [1, num_anchors], so some numpy operations are used to expand the + # dims and concatenate them as needed. + + # These constants can be found by starting from the Box Net's split operation , and for each coordinate, + # walking down in the graph until either an Add or Mul node is found. The second input on this nodes will + # be the anchor data required. + def get_anchor_np(output_idx, op): + node = self.graph.find_descendant_by_op(split.o(0, output_idx), op) + assert node + val = np.squeeze(node.inputs[1].values) + return np.expand_dims(val.flatten(), axis=(0, 2)) + + anchors_y = get_anchor_np(0, "Add") + anchors_x = get_anchor_np(1, "Add") + anchors_h = get_anchor_np(2, "Mul") + anchors_w = get_anchor_np(3, "Mul") + anchors = np.concatenate([anchors_y, anchors_x, anchors_h, anchors_w], axis=2) + return gs.Constant(name="nms/anchors:0", values=anchors) + + self.infer() + + head_names = [] + if self.api == "AutoML": + head_names = ["class_net/", "box_net/"] + if self.api == "TFOD": + head_names = ["/WeightSharedConvolutionalClassHead/", "/WeightSharedConvolutionalBoxHead/"] + + # There are five nodes at the bottom of the graph that provide important connection points: + + # 1. Find the concat node at the end of the class net (multi-scale class predictor) + class_net = find_head_concat(head_names[0]) + class_net_tensor = class_net.outputs[0] + + # 2. Find the concat node at the end of the box net (multi-scale localization predictor) + box_net = find_head_concat(head_names[1]) + box_net_tensor = box_net.outputs[0] + + # 3. Find the split node that separates the box net coordinates and feeds them into the box decoder. + box_net_split = self.graph.find_descendant_by_op(box_net, "Split") + assert box_net_split and len(box_net_split.outputs) == 4 + + # 4. Find the concat node at the end of the box decoder. + box_decoder = self.graph.find_descendant_by_op(box_net_split, "Concat") + assert box_decoder and len(box_decoder.inputs) == 4 + box_decoder_tensor = box_decoder.outputs[0] + + # 5. Find the NMS node. + nms_node = self.graph.find_node_by_op("NonMaxSuppression") + + # Extract NMS Configuration + num_detections = int(nms_node.inputs[2].values) if detections is None else detections + iou_threshold = float(nms_node.inputs[3].values) + score_threshold = float(nms_node.inputs[4].values) if threshold is None else threshold + num_classes = class_net.i().inputs[1].values[-1] + normalized = True if self.api == "TFOD" else False + + # NMS Inputs and Attributes + # NMS expects these shapes for its input tensors: + # box_net: [batch_size, number_boxes, 4] + # class_net: [batch_size, number_boxes, number_classes] + # anchors: [1, number_boxes, 4] (if used) + nms_op = None + nms_attrs = None + nms_inputs = None + if not self.legacy_plugins: + # EfficientNMS TensorRT Plugin + # Fusing the decoder will always be faster, so this is the default NMS method supported. In this case, + # three inputs are given to the NMS TensorRT node: + # - The box predictions (from the Box Net node found above) + # - The class predictions (from the Class Net node found above) + # - The default anchor coordinates (from the extracted anchor constants) + # As the original tensors from EfficientDet will be used, the NMS code type is set to 1 (Center+Size), + # because this is the internal box coding format used by the network. + anchors_tensor = extract_anchors_tensor(box_net_split) + nms_inputs = [box_net_tensor, class_net_tensor, anchors_tensor] + nms_op = "EfficientNMS_TRT" + nms_attrs = { + 'plugin_version': "1", + 'background_class': -1, + 'max_output_boxes': num_detections, + 'score_threshold': max(0.01, score_threshold), # Keep threshold to at least 0.01 for better efficiency + 'iou_threshold': iou_threshold, + 'score_activation': True, + 'box_coding': 1, + } + nms_output_classes_dtype = np.int32 + else: + # BatchedNMS TensorRT Plugin + # Alternatively, the ONNX box decoder can be used. This will be slower, as more element-wise and non-fused + # operations will need to be performed by TensorRT. However, it's easier to implement, so it is shown here + # for reference. In this case, only two inputs are given to the NMS TensorRT node: + # - The box predictions (already decoded through the ONNX Box Decoder node) + # - The class predictions (from the Class Net node found above, but also needs to pass through a sigmoid) + # This time, the box predictions will have the coordinate coding from the ONNX box decoder, which matches + # what the BatchedNMS plugin uses. + + if self.api == "AutoML": + # The default boxes tensor has shape [batch_size, number_boxes, 4]. This will insert a "1" dimension + # in the second axis, to become [batch_size, number_boxes, 1, 4], the shape that BatchedNMS expects. + box_decoder_tensor = self.graph.unsqueeze("nms/box_net_reshape", box_decoder_tensor, axes=[2])[0] + if self.api == "TFOD": + # The default boxes tensor has shape [4, number_boxes]. This will transpose and insert a "1" dimension + # in the 0 and 2 axes, to become [1, number_boxes, 1, 4], the shape that BatchedNMS expects. + box_decoder_tensor = self.graph.transpose("nms/box_decoder_transpose", box_decoder_tensor, perm=[1, 0]) + box_decoder_tensor = self.graph.unsqueeze("nms/box_decoder_reshape", box_decoder_tensor, axes=[0, 2])[0] + + # BatchedNMS also expects the classes tensor to be already activated, in the case of EfficientDet, this is + # through a Sigmoid op. + class_net_tensor = self.graph.sigmoid("nms/class_net_sigmoid", class_net_tensor)[0] + + nms_inputs = [box_decoder_tensor, class_net_tensor] + nms_op = "BatchedNMS_TRT" + nms_attrs = { + 'plugin_version': "1", + 'shareLocation': True, + 'backgroundLabelId': -1, + 'numClasses': num_classes, + 'topK': 1024, + 'keepTopK': num_detections, + 'scoreThreshold': score_threshold, + 'iouThreshold': iou_threshold, + 'isNormalized': normalized, + 'clipBoxes': False, + # 'scoreBits': 10, # Some versions of the plugin may need this parameter. If so, uncomment this line. + } + nms_output_classes_dtype = np.float32 + + # NMS Outputs + nms_output_num_detections = gs.Variable(name="num_detections", dtype=np.int32, shape=[self.batch_size, 1]) + nms_output_boxes = gs.Variable(name="detection_boxes", dtype=np.float32, + shape=[self.batch_size, num_detections, 4]) + nms_output_scores = gs.Variable(name="detection_scores", dtype=np.float32, + shape=[self.batch_size, num_detections]) + nms_output_classes = gs.Variable(name="detection_classes", dtype=nms_output_classes_dtype, + shape=[self.batch_size, num_detections]) + + nms_outputs = [nms_output_num_detections, nms_output_boxes, nms_output_scores, nms_output_classes] + + # Create the NMS Plugin node with the selected inputs. The outputs of the node will also become the final + # outputs of the graph. + self.graph.plugin( + op=nms_op, + name="nms/non_maximum_suppression", + inputs=nms_inputs, + outputs=nms_outputs, + attrs=nms_attrs) + log.info("Created NMS plugin '{}' with attributes: {}".format(nms_op, nms_attrs)) + + self.graph.outputs = nms_outputs + + self.infer() + + +def main(args): + effdet_gs = EfficientDetGraphSurgeon(args.saved_model, args.legacy_plugins) + if args.tf2onnx: + effdet_gs.save(args.tf2onnx) + effdet_gs.update_preprocessor(args.input_shape) + effdet_gs.update_network() + effdet_gs.update_nms(args.nms_threshold, args.nms_detections) + effdet_gs.save(args.onnx) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-m", "--saved_model", help="The TensorFlow saved model directory to load") + parser.add_argument("-o", "--onnx", help="The output ONNX model file to write") + parser.add_argument("-i", "--input_shape", default="1,512,512,3", + help="Set the input shape of the graph, as comma-separated dimensions in NCHW or NHWC format, " + "default: 1,512,512,3") + parser.add_argument("-t", "--nms_threshold", type=float, help="Override the score threshold for the NMS op, " + "default: use the original value in the model") + parser.add_argument("-d", "--nms_detections", type=int, help="Override the max detections for the NMS op, " + "default: use the original value in the model") + parser.add_argument("--legacy_plugins", action="store_true", help="Use legacy plugins for support on TensorRT " + "versions lower than 8.0.1") + parser.add_argument("--tf2onnx", help="The path where to save the intermediate ONNX graph generated by tf2onnx, " + "useful for debugging purposes, default: not saved") + args = parser.parse_args() + if not all([args.saved_model, args.onnx]): + parser.print_help() + print("\nThese arguments are required: --saved_model and --onnx") + sys.exit(1) + main(args) diff --git a/samples/python/efficientdet/eval_coco.py b/samples/python/efficientdet/eval_coco.py new file mode 100644 index 00000000..30e04484 --- /dev/null +++ b/samples/python/efficientdet/eval_coco.py @@ -0,0 +1,79 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 os +import sys +import argparse + +import numpy as np + +from infer import TensorRTInfer +from image_batcher import ImageBatcher + + +def main(args): + automl_path = os.path.realpath(args.automl_path) + sys.path.insert(1, os.path.join(automl_path, "efficientdet")) + try: + import coco_metric + except ImportError: + print("Could not import the 'coco_metric' module from AutoML. Searching in: {}".format(automl_path)) + print("Please clone the repository https://github.com/google/automl and provide its path with --automl_path.") + sys.exit(1) + + trt_infer = TensorRTInfer(args.engine) + batcher = ImageBatcher(args.input, *trt_infer.input_spec()) + evaluator = coco_metric.EvaluationMetric(filename=args.annotations) + for batch, images, scales in batcher.get_batch(): + print("Processing Image {} / {}".format(batcher.image_index, batcher.num_images), end="\r") + detections = trt_infer.infer(batch, scales, args.nms_threshold) + coco_det = np.zeros((len(images), max([len(d) for d in detections]), 7)) + coco_det[:, :, -1] = -1 + for i in range(len(images)): + for n in range(len(detections[i])): + source_id = int(os.path.splitext(os.path.basename(images[i]))[0]) + det = detections[i][n] + coco_det[i][n] = [ + source_id, + det['xmin'], + det['ymin'], + det['xmax'] - det['xmin'], + det['ymax'] - det['ymin'], + det['score'], + det['class'] + 1, # The COCO evaluator expects class 0 to be background, so offset by 1 + ] + evaluator.update_state(None, coco_det) + print() + evaluator.result(100) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-e", "--engine", help="The TensorRT engine to infer with") + parser.add_argument("-i", "--input", + help="The input to infer, either a single image path, or a directory of images") + parser.add_argument("-a", "--annotations", help="Set the path to the COCO 'instances_val2017.json' file") + parser.add_argument("-p", "--automl_path", default="./automl", + help="Set the path where to find the AutoML repository, from " + "https://github.com/google/automl. Default: ./automl") + parser.add_argument("-t", "--nms_threshold", type=float, help="Override the score threshold for the NMS operation, " + "if higher than the threshold in the engine.") + args = parser.parse_args() + if not all([args.engine, args.input, args.annotations]): + parser.print_help() + print("\nThese arguments are required: --engine --input and --annotations") + sys.exit(1) + main(args) diff --git a/samples/python/efficientdet/image_batcher.py b/samples/python/efficientdet/image_batcher.py new file mode 100644 index 00000000..7f1b30c8 --- /dev/null +++ b/samples/python/efficientdet/image_batcher.py @@ -0,0 +1,164 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 os +import sys + +import numpy as np +from PIL import Image + + +class ImageBatcher: + """ + Creates batches of pre-processed images. + """ + + def __init__(self, input, shape, dtype, max_num_images=None, exact_batches=False, preprocessor="EfficientDet"): + """ + :param input: The input directory to read images from. + :param shape: The tensor shape of the batch to prepare, either in NCHW or NHWC format. + :param dtype: The (numpy) datatype to cast the batched data to. + :param max_num_images: The maximum number of images to read from the directory. + :param exact_batches: This defines how to handle a number of images that is not an exact multiple of the batch + size. If false, it will pad the final batch with zeros to reach the batch size. If true, it will *remove* the + last few images in excess of a batch size multiple, to guarantee batches are exact (useful for calibration). + :param preprocessor: Set the preprocessor to use, depending on which network is being used. + """ + # Find images in the given input path + input = os.path.realpath(input) + self.images = [] + + extensions = [".jpg", ".jpeg", ".png", ".bmp"] + + def is_image(path): + return os.path.isfile(path) and os.path.splitext(path)[1].lower() in extensions + + if os.path.isdir(input): + self.images = [os.path.join(input, f) for f in os.listdir(input) if is_image(os.path.join(input, f))] + self.images.sort() + elif os.path.isfile(input): + if is_image(input): + self.images.append(input) + self.num_images = len(self.images) + if self.num_images < 1: + print("No valid {} images found in {}".format("/".join(extensions), input)) + sys.exit(1) + + # Handle Tensor Shape + self.dtype = dtype + self.shape = shape + assert len(self.shape) == 4 + self.batch_size = shape[0] + assert self.batch_size > 0 + self.format = None + self.width = -1 + self.height = -1 + if self.shape[1] == 3: + self.format = "NCHW" + self.height = self.shape[2] + self.width = self.shape[3] + elif self.shape[3] == 3: + self.format = "NHWC" + self.height = self.shape[1] + self.width = self.shape[2] + assert all([self.format, self.width > 0, self.height > 0]) + + # Adapt the number of images as needed + if max_num_images and 0 < max_num_images < len(self.images): + self.num_images = max_num_images + if exact_batches: + self.num_images = self.batch_size * (self.num_images // self.batch_size) + if self.num_images < 1: + print("Not enough images to create batches") + sys.exit(1) + self.images = self.images[0:self.num_images] + + # Subdivide the list of images into batches + self.num_batches = 1 + int((self.num_images - 1) / self.batch_size) + self.batches = [] + for i in range(self.num_batches): + start = i * self.batch_size + end = min(start + self.batch_size, self.num_images) + self.batches.append(self.images[start:end]) + + # Indices + self.image_index = 0 + self.batch_index = 0 + + self.preprocessor = preprocessor + + def preprocess_image(self, image_path): + """ + The image preprocessor loads an image from disk and prepares it as needed for batching. This includes padding, + resizing, normalization, data type casting, and transposing. + This Image Batcher implements one algorithm for now: + * EfficientDet: Resizes and pads the image to fit the input size. + :param image_path: The path to the image on disk to load. + :return: Two values: A numpy array holding the image sample, ready to be contacatenated into the rest of the + batch, and the resize scale used, if any. + """ + + def resize_pad(image, pad_color=(0, 0, 0)): + """ + A subroutine to implement padding and resizing. This will resize the image to fit fully within the input + size, and pads the remaining bottom-right portions with the value provided. + :param image: The PIL image object + :pad_color: The RGB values to use for the padded area. Default: Black/Zeros. + :return: Two values: The PIL image object already padded and cropped, and the resize scale used. + """ + width, height = image.size + width_scale = width / self.width + height_scale = height / self.height + scale = 1.0 / max(width_scale, height_scale) + image = image.resize((round(width * scale), round(height * scale)), resample=Image.BILINEAR) + pad = Image.new("RGB", (self.width, self.height)) + pad.paste(pad_color, [0, 0, self.width, self.height]) + pad.paste(image) + return pad, scale + + scale = None + image = Image.open(image_path) + image = image.convert(mode='RGB') + if self.preprocessor == "EfficientDet": + # For EfficientNet V2: Resize & Pad with ImageNet mean values and keep as [0,255] Normalization + image, scale = resize_pad(image, (124, 116, 104)) + image = np.asarray(image, dtype=self.dtype) + # [0-1] Normalization, Mean subtraction and Std Dev scaling are part of the EfficientDet graph, so + # no need to do it during preprocessing here + else: + print("Preprocessing method {} not supported".format(self.preprocessor)) + sys.exit(1) + if self.format == "NCHW": + image = np.transpose(image, (2, 0, 1)) + return image, scale + + def get_batch(self): + """ + Retrieve the batches. This is a generator object, so you can use it within a loop as: + for batch, images in batcher.get_batch(): + ... + Or outside of a batch with the next() function. + :return: A generator yielding three items per iteration: a numpy array holding a batch of images, the list of + paths to the images loaded within this batch, and the list of resize scales for each image in the batch. + """ + for i, batch_images in enumerate(self.batches): + batch_data = np.zeros(self.shape, dtype=self.dtype) + batch_scales = [None] * len(batch_images) + for i, image in enumerate(batch_images): + self.image_index += 1 + batch_data[i], batch_scales[i] = self.preprocess_image(image) + self.batch_index += 1 + yield batch_data, batch_images, batch_scales diff --git a/samples/python/efficientdet/infer.py b/samples/python/efficientdet/infer.py new file mode 100644 index 00000000..918b9477 --- /dev/null +++ b/samples/python/efficientdet/infer.py @@ -0,0 +1,192 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 os +import sys +import time +import ctypes +import argparse +import numpy as np +import tensorrt as trt + +import pycuda.driver as cuda +import pycuda.autoinit + +from image_batcher import ImageBatcher +from visualize import visualize_detections + + +class TensorRTInfer: + """ + Implements inference for the EfficientDet TensorRT engine. + """ + + def __init__(self, engine_path): + """ + :param engine_path: The path to the serialized engine to load from disk. + """ + # Load TRT engine + self.logger = trt.Logger(trt.Logger.ERROR) + trt.init_libnvinfer_plugins(self.logger, namespace="") + with open(engine_path, "rb") as f, trt.Runtime(self.logger) as runtime: + self.engine = runtime.deserialize_cuda_engine(f.read()) + self.context = self.engine.create_execution_context() + assert self.engine + assert self.context + + # Setup I/O bindings + self.inputs = [] + self.outputs = [] + self.allocations = [] + for i in range(self.engine.num_bindings): + is_input = False + if self.engine.binding_is_input(i): + is_input = True + name = self.engine.get_binding_name(i) + dtype = self.engine.get_binding_dtype(i) + shape = self.engine.get_binding_shape(i) + if is_input: + self.batch_size = shape[0] + size = np.dtype(trt.nptype(dtype)).itemsize + for s in shape: + size *= s + allocation = cuda.mem_alloc(size) + binding = { + 'index': i, + 'name': name, + 'dtype': np.dtype(trt.nptype(dtype)), + 'shape': list(shape), + 'allocation': allocation, + } + self.allocations.append(allocation) + if self.engine.binding_is_input(i): + self.inputs.append(binding) + else: + self.outputs.append(binding) + + assert self.batch_size > 0 + assert len(self.inputs) > 0 + assert len(self.outputs) > 0 + assert len(self.allocations) > 0 + + def input_spec(self): + """ + Get the specs for the input tensor of the network. Useful to prepare memory allocations. + :return: Two items, the shape of the input tensor and its (numpy) datatype. + """ + return self.inputs[0]['shape'], self.inputs[0]['dtype'] + + def output_spec(self): + """ + Get the specs for the output tensors of the network. Useful to prepare memory allocations. + :return: A list with two items per element, the shape and (numpy) datatype of each output tensor. + """ + specs = [] + for o in self.outputs: + specs.append((o['shape'], o['dtype'])) + return specs + + def infer(self, batch, scales=None, nms_threshold=None): + """ + Execute inference on a batch of images. The images should already be batched and preprocessed, as prepared by + the ImageBatcher class. Memory copying to and from the GPU device will be performed here. + :param batch: A numpy array holding the image batch. + :param scales: The image resize scales for each image in this batch. Default: No scale postprocessing applied. + :return: A nested list for each image in the batch and each detection in the list. + """ + # Prepare the output data + outputs = [] + for shape, dtype in self.output_spec(): + outputs.append(np.zeros(shape, dtype)) + + # Process I/O and execute the network + cuda.memcpy_htod(self.inputs[0]['allocation'], np.ascontiguousarray(batch)) + self.context.execute_v2(self.allocations) + for o in range(len(outputs)): + cuda.memcpy_dtoh(outputs[o], self.outputs[o]['allocation']) + + # Process the results + nums = outputs[0] + boxes = outputs[1] + scores = outputs[2] + classes = outputs[3] + detections = [] + normalized = (np.max(boxes) < 2.0) + for i in range(self.batch_size): + detections.append([]) + for n in range(int(nums[i])): + scale = self.inputs[0]['shape'][2] if normalized else 1.0 + if scales and i < len(scales): + scale /= scales[i] + if nms_threshold and scores[i][n] < nms_threshold: + continue + detections[i].append({ + 'ymin': boxes[i][n][0] * scale, + 'xmin': boxes[i][n][1] * scale, + 'ymax': boxes[i][n][2] * scale, + 'xmax': boxes[i][n][3] * scale, + 'score': scores[i][n], + 'class': int(classes[i][n]), + }) + return detections + + +def main(args): + output_dir = os.path.realpath(args.output) + os.makedirs(output_dir, exist_ok=True) + + labels = [] + if args.labels: + with open(args.labels) as f: + for i, label in enumerate(f): + labels.append(label.strip()) + + trt_infer = TensorRTInfer(args.engine) + batcher = ImageBatcher(args.input, *trt_infer.input_spec()) + for batch, images, scales in batcher.get_batch(): + print("Processing Image {} / {}".format(batcher.image_index, batcher.num_images), end="\r") + detections = trt_infer.infer(batch, scales, args.nms_threshold) + for i in range(len(images)): + basename = os.path.splitext(os.path.basename(images[i]))[0] + # Image Visualizations + output_path = os.path.join(output_dir, "{}.png".format(basename)) + visualize_detections(images[i], output_path, detections[i], labels) + # Text Results + output_results = "" + for d in detections[i]: + line = [d['xmin'], d['ymin'], d['xmax'], d['ymax'], d['score'], d['class']] + output_results += "\t".join([str(f) for f in line]) + "\n" + with open(os.path.join(args.output, "{}.txt".format(basename)), "w") as f: + f.write(output_results) + print() + print("Finished Processing") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-e", "--engine", default=None, help="The serialized TensorRT engine") + parser.add_argument("-i", "--input", default=None, help="Path to the image or directory to process") + parser.add_argument("-o", "--output", default=None, help="Directory where to save the visualization results") + parser.add_argument("-l", "--labels", default="./labels_coco.txt", help="File to use for reading the class labels " + "from, default: ./labels_coco.txt") + parser.add_argument("-t", "--nms_threshold", type=float, help="Override the score threshold for the NMS operation, " + "if higher than the threshold in the engine.") + args = parser.parse_args() + if not all([args.engine, args.input, args.output]): + parser.print_help() + print("\nThese arguments are required: --engine --input and --output") + sys.exit(1) + main(args) diff --git a/samples/python/efficientdet/labels_coco.txt b/samples/python/efficientdet/labels_coco.txt new file mode 100644 index 00000000..5378c6cd --- /dev/null +++ b/samples/python/efficientdet/labels_coco.txt @@ -0,0 +1,91 @@ +person +bicycle +car +motorcycle +airplane +bus +train +truck +boat +traffic light +fire hydrant +street sign +stop sign +parking meter +bench +bird +cat +dog +horse +sheep +cow +elephant +bear +zebra +giraffe +hat +backpack +umbrella +shoe +eye glasses +handbag +tie +suitcase +frisbee +skis +snowboard +sports ball +kite +baseball bat +baseball glove +skateboard +surfboard +tennis racket +bottle +plate +wine glass +cup +fork +knife +spoon +bowl +banana +apple +sandwich +orange +broccoli +carrot +hot dog +pizza +donut +cake +chair +couch +potted plant +bed +mirror +dining table +window +desk +toilet +door +tv +laptop +mouse +remote +keyboard +cell phone +microwave +oven +toaster +sink +refrigerator +blender +book +clock +vase +scissors +teddy bear +hair drier +toothbrush +hair brush \ No newline at end of file diff --git a/samples/python/efficientdet/onnx_utils.py b/samples/python/efficientdet/onnx_utils.py new file mode 100644 index 00000000..24a9e190 --- /dev/null +++ b/samples/python/efficientdet/onnx_utils.py @@ -0,0 +1,142 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 logging + +import onnx_graphsurgeon as gs + +logging.basicConfig(level=logging.INFO) +logging.getLogger("EfficientDetHelper").setLevel(logging.INFO) +log = logging.getLogger("EfficientDetHelper") + +@gs.Graph.register() +def elt_const(self, op, name, input, value): + """ + Add an element-wise operation to the graph which will operate on the input tensor with the value(s) given. + :param op: The ONNX operation to perform, i.e. "Add" or "Mul". + :param input: The tensor to operate on. + :param value: The value array to operate with. + :param name: The name to use for the node. + """ + input_tensor = input if type(input) is gs.Variable else input[0] + log.debug("Created {} node '{}': {}".format(op, name, value.squeeze())) + const = gs.Constant(name="{}_value:0".format(name), values=value) + return self.layer(name=name, op=op, inputs=[input_tensor, const], outputs=[name + ":0"]) + +@gs.Graph.register() +def unsqueeze(self, name, input, axes=[-1]): + """ + Adds to the graph an Unsqueeze node for the given axes and to the given input. + :param self: The gs.Graph object being extended. + :param name: The name to use for the node. + :param input: The tensor to be "unsqueezed". + :param axes: A list of axes on which to add the new dimension(s). + :return: The first output tensor, to allow chained graph construction. + """ + input_tensor = input if type(input) is gs.Variable else input[0] + log.debug("Created Unsqueeze node '{}': {}".format(name, axes)) + return self.layer(name=name, op="Unsqueeze", inputs=[input_tensor], outputs=[name + ":0"], attrs={'axes': axes}) + +@gs.Graph.register() +def transpose(self, name, input, perm): + """ + Adds to the graph a Transpose node for the given axes permutation and to the given input. + :param self: The gs.Graph object being extended. + :param name: The name to use for the node. + :param input: The tensor to be transposed. + :param perm: A list of axes defining their order after transposing occurs. + :return: The first output tensor, to allow chained graph construction. + """ + input_tensor = input if type(input) is gs.Variable else input[0] + log.debug("Created Transpose node '{}': {}".format(name, perm)) + return self.layer(name=name, op="Transpose", inputs=[input_tensor], outputs=[name + ":0"], attrs={'perm': perm}) + +@gs.Graph.register() +def sigmoid(self, name, input): + """ + Adds to the graph a Sigmoid node for the given input. + :param self: The gs.Graph object being extended. + :param name: The name to use for the node. + :param input: The tensor to be applied to. + :return: The first output tensor, to allow chained graph construction. + """ + input_tensor = input if type(input) is gs.Variable else input[0] + log.debug("Created Sigmoid node '{}'".format(name)) + return self.layer(name=name, op="Sigmoid", inputs=[input_tensor], outputs=[name + ":0"]) + +@gs.Graph.register() +def plugin(self, op, name, inputs, outputs, attrs): + """ + Adds to the graph a TensorRT plugin node with the given name, inputs and outputs. The attrs dictionary holds + attributes to be added to the plugin node. + :param self: The gs.Graph object being extended. + :param op: The registered name for the TensorRT plugin. + :param name: The name to use for the node. + :param inputs: The list of tensors to use an inputs. + :param outputs: The list of tensors to use as outputs. + :param attrs: The dictionary to use as attributes. + :return: The first output tensor, to allow chained graph construction. + """ + input_tensors = inputs if type(inputs) is list else [inputs] + log.debug("Created TRT Plugin node '{}': {}".format(name, attrs)) + return self.layer(op=op, name=name, inputs=input_tensors, outputs=outputs, attrs=attrs) + +@gs.Graph.register() +def find_node_by_op(self, op): + """ + Finds the first node in the graph with the given operation name. + :param self: The gs.Graph object being extended. + :param op: The operation name to search for. + :return: The first node matching that performs that op. + """ + for node in self.nodes: + if node.op == op: + return node + return None + +@gs.Graph.register() +def find_descendant_by_op(self, node, op, depth=10): + """ + Starting from the given node, finds a node lower in the graph matching the given operation name. This is not an + exhaustive graph search, it will take only the first output of each node traversed while searching depth-first. + :param self: The gs.Graph object being extended. + :param node: The node to start searching from. + :param op: The operation name to search for. + :param depth: Stop searching after traversing these many nodes. + :return: The first descendant node matching that performs that op. + """ + for i in range(depth): + node = node.o() + if node.op == op: + return node + return None + +@gs.Graph.register() +def find_ancestor_by_op(self, node, op, depth=10): + """ + Starting from the given node, finds a node higher in the graph matching the given operation name. This is not an + exhaustive graph search, it will take only the first input of each node traversed while searching depth-first. + :param self: The gs.Graph object being extended. + :param node: The node to start searching from. + :param op: The operation name to search for. + :param depth: Stop searching after traversing these many nodes. + :return: The first ancestor node matching that performs that op. + """ + for i in range(depth): + node = node.i() + if node.op == op: + return node + return None \ No newline at end of file diff --git a/samples/python/efficientdet/requirements.txt b/samples/python/efficientdet/requirements.txt new file mode 100644 index 00000000..a57765b4 --- /dev/null +++ b/samples/python/efficientdet/requirements.txt @@ -0,0 +1,6 @@ +numpy>=1.19.4 +pycuda>=2020.1 +Pillow>=6.0.0 +onnx==1.8.1 +onnxruntime==1.8.0 +tf2onnx==1.8.1 \ No newline at end of file diff --git a/samples/python/efficientdet/visualize.py b/samples/python/efficientdet/visualize.py new file mode 100644 index 00000000..838b2fa6 --- /dev/null +++ b/samples/python/efficientdet/visualize.py @@ -0,0 +1,94 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 numpy as np + +import PIL.Image as Image +import PIL.ImageDraw as ImageDraw +import PIL.ImageFont as ImageFont + +COLORS = ['GoldenRod', 'MediumTurquoise', 'GreenYellow', 'SteelBlue', 'DarkSeaGreen', 'SeaShell', 'LightGrey', + 'IndianRed', 'DarkKhaki', 'LawnGreen', 'WhiteSmoke', 'Peru', 'LightCoral', 'FireBrick', 'OldLace', + 'LightBlue', 'SlateGray', 'OliveDrab', 'NavajoWhite', 'PaleVioletRed', 'SpringGreen', 'AliceBlue', 'Violet', + 'DeepSkyBlue', 'Red', 'MediumVioletRed', 'PaleTurquoise', 'Tomato', 'Azure', 'Yellow', 'Cornsilk', + 'Aquamarine', 'CadetBlue', 'CornflowerBlue', 'DodgerBlue', 'Olive', 'Orchid', 'LemonChiffon', 'Sienna', + 'OrangeRed', 'Orange', 'DarkSalmon', 'Magenta', 'Wheat', 'Lime', 'GhostWhite', 'SlateBlue', 'Aqua', + 'MediumAquaMarine', 'LightSlateGrey', 'MediumSeaGreen', 'SandyBrown', 'YellowGreen', 'Plum', 'FloralWhite', + 'LightPink', 'Thistle', 'DarkViolet', 'Pink', 'Crimson', 'Chocolate', 'DarkGrey', 'Ivory', 'PaleGreen', + 'DarkGoldenRod', 'LavenderBlush', 'SlateGrey', 'DeepPink', 'Gold', 'Cyan', 'LightSteelBlue', 'MediumPurple', + 'ForestGreen', 'DarkOrange', 'Tan', 'Salmon', 'PaleGoldenRod', 'LightGreen', 'LightSlateGray', 'HoneyDew', + 'Fuchsia', 'LightSeaGreen', 'DarkOrchid', 'Green', 'Chartreuse', 'LimeGreen', 'AntiqueWhite', 'Beige', + 'Gainsboro', 'Bisque', 'SaddleBrown', 'Silver', 'Lavender', 'Teal', 'LightCyan', 'PapayaWhip', 'Purple', + 'Coral', 'BurlyWood', 'LightGray', 'Snow', 'MistyRose', 'PowderBlue', 'DarkCyan', 'White', 'Turquoise', + 'MediumSlateBlue', 'PeachPuff', 'Moccasin', 'LightSalmon', 'SkyBlue', 'Khaki', 'MediumSpringGreen', + 'BlueViolet', 'MintCream', 'Linen', 'SeaGreen', 'HotPink', 'LightYellow', 'BlanchedAlmond', 'RoyalBlue', + 'RosyBrown', 'MediumOrchid', 'DarkTurquoise', 'LightGoldenRodYellow', 'LightSkyBlue'] + + +def visualize_detections(image_path, output_path, detections, labels=[]): + image = Image.open(image_path).convert(mode='RGB') + draw = ImageDraw.Draw(image) + line_width = 2 + font = ImageFont.load_default() + for d in detections: + color = COLORS[d['class'] % len(COLORS)] + draw.line([(d['xmin'], d['ymin']), (d['xmin'], d['ymax']), (d['xmax'], d['ymax']), (d['xmax'], d['ymin']), + (d['xmin'], d['ymin'])], width=line_width, fill=color) + label = "Class {}".format(d['class']) + if d['class'] < len(labels): + label = "{}".format(labels[d['class']]) + score = d['score'] + text = "{}: {}%".format(label, int(100 * score)) + if score < 0: + text = label + text_width, text_height = font.getsize(text) + text_bottom = max(text_height, d['ymin']) + text_left = d['xmin'] + margin = np.ceil(0.05 * text_height) + draw.rectangle([(text_left, text_bottom - text_height - 2 * margin), (text_left + text_width, text_bottom)], + fill=color) + draw.text((text_left + margin, text_bottom - text_height - margin), text, fill='black', font=font) + if output_path is None: + return image + image.save(output_path) + + +def concat_visualizations(images, names, colors, output_path): + def draw_text(draw, font, text, width, bar_height, offset, color): + text_width, text_height = font.getsize(text) + draw.rectangle([(offset, 0), (offset + width, bar_height)], fill=color) + draw.text((offset + (width - text_width) / 2, text_height - text_height / 2), text, fill='black', font=font) + + bar_height = 18 + width = 0 + height = 0 + for im in images: + width += im.width + height = max(height, im.height) + + concat = Image.new('RGB', (width, height + bar_height)) + draw = ImageDraw.Draw(concat) + font = ImageFont.load_default() + + offset = 0 + for i, im in enumerate(images): + concat.paste(im, (offset, bar_height)) + draw_text(draw, font, names[i], im.width, bar_height, offset, colors[i]) + offset += im.width + + if output_path is None: + return concat + concat.save(output_path) diff --git a/samples/python/efficientnet/README.md b/samples/python/efficientnet/README.md new file mode 100644 index 00000000..9354c1df --- /dev/null +++ b/samples/python/efficientnet/README.md @@ -0,0 +1,276 @@ +# EfficientNet V1 and V2 in TensorRT + +These scripts help with conversion and execution of Google [EfficientNet V1](https://arxiv.org/abs/1905.11946) and [EfficientNet V2](https://arxiv.org/abs/2104.00298) models with [NVIDIA TensorRT](https://developer.nvidia.com/tensorrt). + +## Contents +- [Setup](#setup) +- [Model Conversion](#model-conversion) + * [TensorFlow Saved Model](#tensorflow-saved-model) + * [Create ONNX Graph](#create-onnx-graph) + * [Build TensorRT Engine](#build-tensorrt-engine) + * [Benchmark TensorRT Engine](#benchmark-tensorrt-engine) +- [Inference](#inference) + * [Input Preprocessing](#input-preprocessing) + * [Inference in Python](#inference-in-python) + * [Validate against Ground Truth](#validate-against-ground-truth) + * [Compare against TensorFlow](#compare-against-tensorflow) + +## Setup + +For best results, we recommend running these scripts on an environment with TensorRT >= 8.0.1 and TensorFlow 2.5. + +Install TensorRT as per the [TensorRT Install Guide](https://docs.nvidia.com/deeplearning/tensorrt/install-guide/index.html). You will need to make sure the Python bindings for TensorRT are also installed correctly, these are available by installing the `python3-libnvinfer` and `python3-libnvinfer-dev` packages on your TensorRT download. + +Make sure all other packages listed in `requirements.txt`: + +``` +pip install -r requirements.txt +``` + +You will also need the latest `onnx_graphsurgeon` python module. If not already installed by TensorRT, you can install it manually by running: + +``` +pip install onnx-graphsurgeon --index-url https://pypi.ngc.nvidia.com +``` + +## Model Conversion + +The workflow to convert an EfficientNet model is basically TensorFlow → ONNX → TensorRT, and so parts of this process require TensorFlow to be installed. If you are performing this conversion to run inference on the edge, such as for NVIDIA Jetson devices, it might be easier to do the ONNX conversion on a PC first. + +### TensorFlow Saved Model + +The starting point of conversion is a TensorFlow saved model. This can be exported from your own trained models, or you can download a pre-trained model. This conversion script is compatible with two types of models: + +1. EfficientNet V1 models trained with the [TensorFlow TPU Models](https://github.com/tensorflow/tpu/tree/master/models/official/efficientnet) framework. +2. EfficientNet V2 models trained with the [AutoML](https://github.com/google/automl/tree/master/efficientnetv2) framework. + +#### 1. EfficientNet V1 + +You can download one of the pre-trained saved models from the [EfficientNet TFHub](https://tfhub.dev/google/collections/efficientnet), such as: + +``` +wget https://storage.googleapis.com/tfhub-modules/tensorflow/efficientnet/b0/classification/1.tar.gz +``` + +The contents of this package, when extracted, will hold a saved model ready for conversion. + +Alternatively, if you are training your own model, or if you need to re-export the saved model manually, you will need the training checkpoint (or a pre-trained "ckpt" from the [EfficientNet Repository](https://github.com/tensorflow/tpu/tree/master/models/official/efficientnet) such as [this](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientnet/ckpts/efficientnet-b0.tar.gz)). + +To export a saved model from the checkpoint, clone and install the [TensorFlow TPU Models](https://github.com/tensorflow/tpu) repository, and run: + +``` +cd /path/to/tpu/models/official/efficientnet +python export_model.py \ + --ckpt_dir /path/to/efficientnet-b0 \ + --image_size 224 \ + --model_name efficientnet-b0 \ + --output_tflite /dev/null \ + --noquantize \ + --output_saved_model_dir /path/to/saved_model +``` + +Adapt `--image_size` and `--model_name` according to the checkpoint model being used. The `--ckpt_dir` argument points to the directory holding the checkpoint as described above. The TF saved model will be exported to the path given by `--output_saved_model_dir`. + +#### 2. EfficientNet V2 + +At the time of this writing, there exist no EfficientNet V2 saved models in TFHub yet. So you will need to download a pre-trained checkpoint, or use your own trained model of course. + +To do so, you will need your training checkpoint (or a pre-trained "ckpt" from the [EfficientNet V2 Repository](https://github.com/google/automl/tree/master/efficientnetv2) such as [this](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientnet/v2/efficientnetv2-s.tgz)): + +``` +wget https://storage.googleapis.com/cloud-tpu-checkpoints/efficientnet/v2/efficientnetv2-s.tgz +``` + +To export a saved model from here, clone and install the [AutoML](https://github.com/google/automl) repository, and run: + +``` +cd /path/to/automl/efficientnetv2 +python infer.py \ + --mode tf2bm \ + --model_name efficientnetv2-s \ + --model_dir ../../../models/effnetv2/effnetv2-s/checkpoint/ \ + --export_dir ../../../models/effnetv2/effnetv2-s/checkpoint/saved_model +``` + +Where you should adapt `--model_name` to the corresponding model for the checkpoint used. The `--model_dir` argument should point to the downloaded or trained checkpoint as described above. The exported saved model will then be available in the directory pointed by the `--export_dir` argument. + +### Create ONNX Graph + +To generate an ONNX model file, find the saved model as described above, select a batch size and input size, and run: + +``` +python create_onnx.py \ + --saved_model /path/to/saved_model \ + --onnx /path/to/model.onnx \ + --batch_size 1 \ + --input_size 384 +``` + +You may need to adapt the argument `--input_size` to explicitly define the exact input image dimensions to use in the graph. Consult the model definitions in the corresponding training system, to find the expected input size for the model you are working with. + +This will create the file `model.onnx` which is ready to convert to TensorRT. + +Optionally, you may wish to visualize the resulting ONNX graph with a tool such as [Netron](https://netron.app/). + +### Build TensorRT Engine + +It is possible to build the TensorRT engine directly with `trtexec` using the ONNX graph generated in the previous step. However, the script `build_engine.py` is provided for convenience, as it has been tailored to EfficientNet engine building and calibration. Run `python build_engine.py --help` for details on available settings. + +#### FP16 Precision + +To build the TensorRT engine file with FP16 precision, run: + +``` +python build_engine.py \ + --onnx /path/to/model.onnx \ + --engine /path/to/engine.trt \ + --precision fp16 +``` + +The file `engine.trt` will be created, which can now be used to infer with TensorRT. + +For best results, make sure no other processes are using the GPU during engine build, as it may affect the optimal tactic selection process. + +#### INT8 Precision + +To build and calibrate an engine for INT8 precision, run: + +``` +python build_engine.py \ + --onnx /path/to/model.onnx \ + --engine /path/to/engine.trt \ + --precision int8 \ + --calib_input /path/to/calibration/images \ + --calib_cache /path/to/calibration.cache \ + --calib_preprocessor V2 +``` + +Where `--calib_input` points to a directory with several thousands of images. For example, this could be a subset of the training or validation datasets that were used for the model. It's important that this data represents the runtime data distribution relatively well, therefore, the more images that are used for calibration, the better accuracy that will be achieved in INT8 precision. For ImageNet networks, we have found that 25,000 images gives a good result. + +The `--calib_cache` argument controls where the calibration cache file will be written to. This is useful to keep a cached copy of the calibration results. Next time you need to build the engine for the same network, if this file exists, it will skip the calibration step and use the cached values instead. + +Finally, the `--calib_preprocessor` option sets the preprocessing algorithm to apply on calibration images. Please refer to the [Input Preprocessing](#input-preprocessing) section below for more details. + +Run `python build_engine.py --help` for additional build options. + +### Benchmark TensorRT Engine + +Optionally, you can obtain execution timing information for the built engine by using the `trtexec` utility, as: + +``` +trtexec \ + --loadEngine=/path/to/engine.trt \ + --useCudaGraph --noDataTransfers \ + --iterations=100 --avgRuns=100 +``` + +If it's not already in your `$PATH`, the `trtexec` binary is usually found in `/usr/src/tensorrt/bin/trtexec`, depending on your TensorRT installation method. + +An inference benchmark will run, with GPU Compute latency times printed out to the console. Depending on the version of TensorRT, you should see something similar to: + +``` +GPU Compute Time: min = 1.79895 ms, max = 1.9209 ms, mean = 1.80589 ms, median = 1.80493 ms, percentile(99%) = 1.81396 ms +``` + +## Inference + +For optimal performance, inference should be done in a C++ application that takes advantage of CUDA Graphs to launch the inference request. Alternatively, the TensorRT engine built with this process can also be executed through either [Triton Inference Server](https://developer.nvidia.com/nvidia-triton-inference-server) or [DeepStream SDK](https://developer.nvidia.com/deepstream-sdk). + +However, for convenience, a python inference script is provided here for quick testing of the built TensorRT engine. + +### Input Preprocessing + +An important concept for computer vision models is the preprocessing applied to an image before feeding it to the classifier network. The various EfficientNet models supported by this converter use different preprocessing algorithms. + +We have implemented three different preprocessor algorithms, as defined in `image_batcher.py`. They are: + +| **Preprocessing** | **Resizing** | **Normalization** | **Mean Subtract** | +| ----------------- | ------------------------ | ----------------- | ----------------- | +| **V2** | Bilinear Resize | [-1 to +1] Range | No | +| **V1** | Bicubic Resize + PadCrop | [0 to +1] Range | No | +| **V1MS** | Bicubic Resize + PadCrop | [0 to +1] Range | Yes | + +**V2:** This is the preprocessor to be used with all EfficientNet V2 models. EfficientNet V2 does not require mean subtraction, so it is never performed for these models. + +**V1:** This is the default preprocessor to be used with most EfficientNet V1 models. EfficientNet V1 normally expects mean subtraction to be applied. However, some TensorFlow saved models, such as those downloaded from TFHub, already perform this operation within the graph itself, so it is not required to do it during preprocessing. + +**V1MS:** Depending on the saved model exporter, some EfficientNet V1 models may not have the integrated mean subtraction. This is often the case with models exported from the pre-trained *checkpoints*. For those cases, this preprocessor will apply mean subtraction during preprocessing. + +These are the supported values for `--preprocessor` and `--calib_preprocessor` arguments used throughout these scripts. Note that choosing an incorrect preprocessor for a model will considerably impact its accuracy. Please take a moment to choose the correct preprocessor to use before performing inference or validation of a model. + +### Inference in Python + +To classify a set of images with TensorRT, run: + +``` +python infer.py \ + --engine /paht/to/engine.trt \ + --input /path/to/images \ + --preprocessor V2 +``` + +Where the input path can be either a single image file, or a directory of jpg/png/bmp images. The classification results will be printed out to the console, one image per line, as: + +``` + +``` + +You can also redirect these results to a file, and optionally set a separator character (such as for CSV file creation): + +``` +python infer.py \ + --engine /path/to/engine.trt \ + --input /path/to/ILSVRC2012_img_val \ + --preprocessor V2 \ + --separator ',' > results.csv +``` + +### Validate against Ground Truth + +To validate the TensorRT inference results accuracy against ground truth labels, run: + +``` +python eval_gt.py \ + --engine /path/to/engine.trt \ + --annotations /path/to/annotations.txt \ + --input /path/to/images \ + --preprocessor V2 +``` + +The labels file is expected to have one line per image, where the first column is the image filename, and the second column is the ground truth class label. For example: + +``` +val/ILSVRC2012_val_00000001.JPEG 65 +val/ILSVRC2012_val_00000002.JPEG 970 +val/ILSVRC2012_val_00000003.JPEG 230 +val/ILSVRC2012_val_00000004.JPEG 809 +[...] +``` + +Upon a successful run of `EfficientNet V2-S` on the `ILSVRC2012_img_val` [ImageNet](https://www.image-net.org/download.php) dataset, for example, you should see something like: + +``` +Top-1 Accuracy: 83.710% +Top-5 Accuracy: 96.615% +``` + +### Compare against TensorFlow + +Another method to validate the engine is to compare the TensorRT inference results with what TensorFlow produces, to make sure both frameworks give similar results. For this, run: + +``` +python compare_tf.py \ + --engine /path/to/engine.trt \ + --saved_model /path/to/saved_model \ + --input /path/to/images \ + --preprocessor V2 +``` + +This can be performed on any set of images, no ground truth is required. The script executes both the TensorFlow saved model and the TensorRT engine simultaneously on the given input images. It then computes the class prediction similarity and RMSE in confidence scores between both outputs. + +Upon a successful run, you should see something like: + +``` +Matching Top-1 class predictions for 4999 out of 5000 images: 99.98% +RMSE between TensorFlow and TensorRT confidence scores: 0.006 +``` diff --git a/samples/python/efficientnet/build_engine.py b/samples/python/efficientnet/build_engine.py new file mode 100644 index 00000000..e615850d --- /dev/null +++ b/samples/python/efficientnet/build_engine.py @@ -0,0 +1,237 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 os +import sys +import logging +import argparse + +import numpy as np +import tensorrt as trt +import pycuda.driver as cuda +import pycuda.autoinit + +from image_batcher import ImageBatcher + +logging.basicConfig(level=logging.INFO) +logging.getLogger("EngineBuilder").setLevel(logging.INFO) +log = logging.getLogger("EngineBuilder") + + +class EngineCalibrator(trt.IInt8EntropyCalibrator2): + """ + Implements the INT8 Entropy Calibrator 2. + """ + + def __init__(self, cache_file): + """ + :param cache_file: The location of the cache file. + """ + super().__init__() + self.cache_file = cache_file + self.image_batcher = None + self.batch_allocation = None + self.batch_generator = None + + def set_image_batcher(self, image_batcher: ImageBatcher): + """ + Define the image batcher to use, if any. If using only the cache file, an image batcher doesn't need + to be defined. + :param image_batcher: The ImageBatcher object + """ + self.image_batcher = image_batcher + size = int(np.dtype(self.image_batcher.dtype).itemsize * np.prod(self.image_batcher.shape)) + self.batch_allocation = cuda.mem_alloc(size) + self.batch_generator = self.image_batcher.get_batch() + + def get_batch_size(self): + """ + Overrides from trt.IInt8EntropyCalibrator2. + Get the batch size to use for calibration. + :return: Batch size. + """ + if self.image_batcher: + return self.image_batcher.batch_size + return 1 + + def get_batch(self, names): + """ + Overrides from trt.IInt8EntropyCalibrator2. + Get the next batch to use for calibration, as a list of device memory pointers. + :param names: The names of the inputs, if useful to define the order of inputs. + :return: A list of int-casted memory pointers. + """ + if not self.image_batcher: + return None + try: + batch, _ = next(self.batch_generator) + log.info("Calibrating image {} / {}".format(self.image_batcher.image_index, self.image_batcher.num_images)) + cuda.memcpy_htod(self.batch_allocation, np.ascontiguousarray(batch)) + return [int(self.batch_allocation)] + except StopIteration: + log.info("Finished calibration batches") + return None + + def read_calibration_cache(self): + """ + Overrides from trt.IInt8EntropyCalibrator2. + Read the calibration cache file stored on disk, if it exists. + :return: The contents of the cache file, if any. + """ + if os.path.exists(self.cache_file): + with open(self.cache_file, "rb") as f: + log.info("Using calibration cache file: {}".format(self.cache_file)) + return f.read() + + def write_calibration_cache(self, cache): + """ + Overrides from trt.IInt8EntropyCalibrator2. + Store the calibration cache to a file on disk. + :param cache: The contents of the calibration cache to store. + """ + with open(self.cache_file, "wb") as f: + log.info("Writing calibration cache data to: {}".format(self.cache_file)) + f.write(cache) + + +class EngineBuilder: + """ + Parses an ONNX graph and builds a TensorRT engine from it. + """ + + def __init__(self, verbose=False): + """ + :param verbose: If enabled, a higher verbosity level will be set on the TensorRT logger. + """ + self.trt_logger = trt.Logger(trt.Logger.INFO) + if verbose: + self.trt_logger.min_severity = trt.Logger.Severity.VERBOSE + + trt.init_libnvinfer_plugins(self.trt_logger, namespace="") + + self.builder = trt.Builder(self.trt_logger) + self.config = self.builder.create_builder_config() + self.config.max_workspace_size = 8 * (2 ** 30) # 8 GB + + self.batch_size = None + self.network = None + self.parser = None + + def create_network(self, onnx_path): + """ + Parse the ONNX graph and create the corresponding TensorRT network definition. + :param onnx_path: The path to the ONNX graph to load. + """ + network_flags = (1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)) + + self.network = self.builder.create_network(network_flags) + self.parser = trt.OnnxParser(self.network, self.trt_logger) + + onnx_path = os.path.realpath(onnx_path) + with open(onnx_path, "rb") as f: + if not self.parser.parse(f.read()): + log.error("Failed to load ONNX file: {}".format(onnx_path)) + for error in range(self.parser.num_errors): + log.error(self.parser.get_error(error)) + sys.exit(1) + + inputs = [self.network.get_input(i) for i in range(self.network.num_inputs)] + outputs = [self.network.get_output(i) for i in range(self.network.num_outputs)] + + log.info("Network Description") + for input in inputs: + self.batch_size = input.shape[0] + log.info("Input '{}' with shape {} and dtype {}".format(input.name, input.shape, input.dtype)) + for output in outputs: + log.info("Output '{}' with shape {} and dtype {}".format(output.name, output.shape, output.dtype)) + assert self.batch_size > 0 + self.builder.max_batch_size = self.batch_size + + def create_engine(self, engine_path, precision, calib_input=None, calib_cache=None, calib_num_images=25000, + calib_batch_size=8, calib_preprocessor=None): + """ + Build the TensorRT engine and serialize it to disk. + :param engine_path: The path where to serialize the engine to. + :param precision: The datatype to use for the engine, either 'fp32', 'fp16' or 'int8'. + :param calib_input: The path to a directory holding the calibration images. + :param calib_cache: The path where to write the calibration cache to, or if it already exists, load it from. + :param calib_num_images: The maximum number of images to use for calibration. + :param calib_batch_size: The batch size to use for the calibration process. + :param calib_preprocessor: The ImageBatcher preprocessor algorithm to use. + """ + engine_path = os.path.realpath(engine_path) + engine_dir = os.path.dirname(engine_path) + os.makedirs(engine_dir, exist_ok=True) + log.info("Building {} Engine in {}".format(precision, engine_path)) + + inputs = [self.network.get_input(i) for i in range(self.network.num_inputs)] + + if precision == "fp16": + if not self.builder.platform_has_fast_fp16: + log.warning("FP16 is not supported natively on this platform/device") + else: + self.config.set_flag(trt.BuilderFlag.FP16) + elif precision == "int8": + if not self.builder.platform_has_fast_int8: + log.warning("INT8 is not supported natively on this platform/device") + else: + self.config.set_flag(trt.BuilderFlag.INT8) + self.config.int8_calibrator = EngineCalibrator(calib_cache) + if not os.path.exists(calib_cache): + calib_shape = [calib_batch_size] + list(inputs[0].shape[1:]) + calib_dtype = trt.nptype(inputs[0].dtype) + self.config.int8_calibrator.set_image_batcher( + ImageBatcher(calib_input, calib_shape, calib_dtype, max_num_images=calib_num_images, + exact_batches=True, preprocessor=calib_preprocessor)) + + with self.builder.build_engine(self.network, self.config) as engine, open(engine_path, "wb") as f: + log.info("Serializing engine to file: {:}".format(engine_path)) + f.write(engine.serialize()) + + +def main(args): + builder = EngineBuilder(args.verbose) + builder.create_network(args.onnx) + builder.create_engine(args.engine, args.precision, args.calib_input, args.calib_cache, args.calib_num_images, + args.calib_batch_size, args.calib_preprocessor) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-o", "--onnx", help="The input ONNX model file to load") + parser.add_argument("-e", "--engine", help="The output path for the TRT engine") + parser.add_argument("-p", "--precision", default="fp16", choices=["fp32", "fp16", "int8"], + help="The precision mode to build in, either 'fp32', 'fp16' or 'int8', default: 'fp16'") + parser.add_argument("-v", "--verbose", action="store_true", help="Enable more verbose log output") + parser.add_argument("--calib_input", help="The directory holding images to use for calibration") + parser.add_argument("--calib_cache", default="./calibration.cache", + help="The file path for INT8 calibration cache to use, default: ./calibration.cache") + parser.add_argument("--calib_num_images", default=25000, type=int, + help="The maximum number of images to use for calibration, default: 25000") + parser.add_argument("--calib_batch_size", default=8, type=int, + help="The batch size for the calibration process, default: 1") + parser.add_argument("--calib_preprocessor", default="V2", choices=["V1", "V1MS", "V2"], + help="Set the calibration image preprocessor to use, either 'V2', 'V1' or 'V1MS', default: V2") + args = parser.parse_args() + if not all([args.onnx, args.engine]): + parser.print_help() + log.error("These arguments are required: --onnx and --engine") + sys.exit(1) + if args.precision == "int8" and not any([args.calib_input, args.calib_cache]): + parser.print_help() + log.error("When building in int8 precision, either --calib_input or --calib_cache are required") + sys.exit(1) + main(args) diff --git a/samples/python/efficientnet/compare_tf.py b/samples/python/efficientnet/compare_tf.py new file mode 100644 index 00000000..2985caa0 --- /dev/null +++ b/samples/python/efficientnet/compare_tf.py @@ -0,0 +1,150 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 os +import sys +import argparse + +import numpy as np +import tensorflow as tf + +from infer import TensorRTInfer +from image_batcher import ImageBatcher + + +class TensorFlowInfer: + """ + Implements TensorFlow inference of a saved model, following the same API as the TensorRTInfer class. + """ + + def __init__(self, saved_model_path): + gpus = tf.config.experimental.list_physical_devices('GPU') + for gpu in gpus: + tf.config.experimental.set_memory_growth(gpu, True) + + self.model = tf.saved_model.load(saved_model_path) + self.pred_fn = self.model.signatures['serving_default'] + + # Setup I/O bindings + self.inputs = [] + fn_inputs = self.pred_fn.structured_input_signature[1] + for i, input in enumerate(list(fn_inputs.values())): + self.inputs.append({ + 'index': i, + 'name': input.name, + 'dtype': np.dtype(input.dtype.as_numpy_dtype()), + 'shape': input.shape.as_list(), + }) + self.outputs = [] + fn_outputs = self.pred_fn.structured_outputs + for i, output in enumerate(list(fn_outputs.values())): + self.outputs.append({ + 'index': i, + 'name': output.name, + 'dtype': np.dtype(output.dtype.as_numpy_dtype()), + 'shape': output.shape.as_list(), + }) + + def input_spec(self): + return self.inputs[0]['shape'], self.inputs[0]['dtype'] + + def output_spec(self): + return self.outputs[0]['shape'], self.outputs[0]['dtype'] + + def infer(self, batch, top=1): + # Process I/O and execute the network + input = {self.inputs[0]['name']: tf.convert_to_tensor(batch)} + output = self.pred_fn(**input) + output = output[self.outputs[0]['name']].numpy() + + # Read and process the results + classes = np.argmax(output, axis=1) + scores = np.max(output, axis=1) + top = max(top, output.shape[1]) + top_classes = np.flip(np.argsort(output, axis=1), axis=1)[:, 0:top] + top_scores = np.flip(np.sort(output, axis=1), axis=1)[:, 0:top] + + return classes, scores, [top_classes, top_scores] + + +def main(args): + # Initialize TRT and TF infer objects. + tf_infer = TensorFlowInfer(args.saved_model) + trt_infer = TensorRTInfer(args.engine) + + batcher = ImageBatcher(args.input, *trt_infer.input_spec(), max_num_images=args.num_images, + preprocessor=args.preprocessor) + + # Make sure both systems use the same input spec, so we can use the exact same image batches with both + tf_shape, tf_dtype = tf_infer.input_spec() + trt_shape, trt_dtype = trt_infer.input_spec() + if trt_dtype != tf_dtype: + print("Input datatype does not match") + print("TRT Engine Input Dtype: {} {}".format(trt_dtype)) + print("TF Saved Model Input Dtype: {} {}".format(tf_dtype)) + print("Please use the same TensorFlow saved model that the TensorRT engine was built with") + sys.exit(1) + + if (tf_shape[1] and trt_shape[1] != tf_shape[1]) or (tf_shape[2] and trt_shape[2] != tf_shape[2]): + print("Input shapes do not match") + print("TRT Engine Input Shape: {} {}".format(trt_shape[1:])) + print("TF Saved Model Input Shape: {} {}".format(tf_shape[1:])) + print("Please use the same TensorFlow saved model that the TensorRT engine was built with") + sys.exit(1) + + match = 0 + error = 0 + for batch, images in batcher.get_batch(): + # Run inference on the same batch with both inference systems + tf_classes, tf_scores, _ = tf_infer.infer(batch) + trt_classes, trt_scores, _ = trt_infer.infer(batch) + + # The last batch may not have all image slots filled, so limit the results to only the amount of actual images + tf_classes = tf_classes[0:len(images)] + tf_scores = tf_scores[0:len(images)] + trt_classes = trt_classes[0:len(images)] + trt_scores = trt_scores[0:len(images)] + + # Track how many images match on top-1 class id predictions + match += np.sum(trt_classes == tf_classes) + # Track the mean square error in confidence score + error += np.sum((trt_scores - tf_scores) * (trt_scores - tf_scores)) + + print("Processing {} / {} images: {:.2f}% match ".format(batcher.image_index, batcher.num_images, + (100 * (match / batcher.image_index))), end="\r") + + print() + pc = 100 * (match / batcher.num_images) + print("Matching Top-1 class predictions for {} out of {} images: {:.2f}%".format(match, batcher.num_images, pc)) + avgerror = np.sqrt(error / batcher.num_images) + print("RMSE between TensorFlow and TensorRT confidence scores: {:.3f}".format(avgerror)) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-e", "--engine", help="The TensorRT engine to infer with") + parser.add_argument("-m", "--saved_model", help="The TensorFlow saved model path to validate against") + parser.add_argument("-i", "--input", + help="The input to infer, either a single image path, or a directory of images") + parser.add_argument("-n", "--num_images", default=5000, type=int, + help="The maximum number of images to use for validation, default: 5000") + parser.add_argument("-p", "--preprocessor", default="V2", choices=["V1", "V1MS", "V2"], + help="Select the image preprocessor to use, either 'V2', 'V1' or 'V1MS', default: V2") + args = parser.parse_args() + if not all([args.engine, args.saved_model, args.input]): + parser.print_help() + sys.exit(1) + main(args) diff --git a/samples/python/efficientnet/create_onnx.py b/samples/python/efficientnet/create_onnx.py new file mode 100644 index 00000000..d69126ad --- /dev/null +++ b/samples/python/efficientnet/create_onnx.py @@ -0,0 +1,97 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 os +import sys +import argparse + +import onnx +import onnx_graphsurgeon as gs +from onnx import shape_inference + +import numpy as np +import tensorflow as tf +from tf2onnx import tfonnx, optimizer, tf_loader + + +def main(args): + # Load saved model + saved_model_path = os.path.realpath(args.saved_model) + assert os.path.isdir(saved_model_path) + graph_def, inputs, outputs = tf_loader.from_saved_model(saved_model_path, None, None, "serve", ["serving_default"]) + with tf.Graph().as_default() as tf_graph: + tf.import_graph_def(graph_def, name="") + with tf_loader.tf_session(graph=tf_graph): + onnx_graph = tfonnx.process_tf_graph(tf_graph, input_names=inputs, output_names=outputs, opset=11) + onnx_model = optimizer.optimize_graph(onnx_graph).make_model("Converted from {}".format(saved_model_path)) + graph = gs.import_onnx(onnx_model) + assert graph + print() + print("ONNX graph created successfully") + + # Set the I/O tensor shapes + graph.inputs[0].shape[0] = args.batch_size + graph.outputs[0].shape[0] = args.batch_size + if args.input_size and args.input_size > 0: + if graph.inputs[0].shape[3] == 3: + # Format NHWC + graph.inputs[0].shape[1] = args.input_size + graph.inputs[0].shape[2] = args.input_size + elif graph.inputs[0].shape[1] == 3: + # Format NCHW + graph.inputs[0].shape[2] = args.input_size + graph.inputs[0].shape[3] = args.input_size + print("ONNX input named '{}' with shape {}".format(graph.inputs[0].name, graph.inputs[0].shape)) + print("ONNX output named '{}' with shape {}".format(graph.outputs[0].name, graph.outputs[0].shape)) + for i in range(4): + if type(graph.inputs[0].shape[i]) != int or graph.inputs[0].shape[i] <= 0: + print("The input shape of the graph is invalid, try overriding it by giving a fixed size with --input_size") + sys.exit(1) + + # Fix Clip Nodes (ReLU6) + for node in [n for n in graph.nodes if n.op == "Clip"]: + for input in node.inputs[1:]: + # In TensorRT, the min/max inputs on a Clip op *must* have fp32 datatype + input.values = np.float32(input.values) + + # Run tensor shape inference + graph.cleanup().toposort() + model = shape_inference.infer_shapes(gs.export_onnx(graph)) + graph = gs.import_onnx(model) + + # Save updated model + graph.cleanup().toposort() + model = gs.export_onnx(graph) + onnx_path = os.path.realpath(args.onnx) + os.makedirs(os.path.dirname(onnx_path), exist_ok=True) + onnx.save(model, onnx_path) + engine_path = os.path.join(os.path.dirname(onnx_path), "engine.trt") + print("ONNX model saved to {}".format(onnx_path)) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-m", "--saved_model", help="The TensorFlow saved model directory to load") + parser.add_argument("-o", "--onnx", help="The output ONNX model file to write") + parser.add_argument("-b", "--batch_size", type=int, default=1, help="Set the batch size, default: 1") + parser.add_argument("-i", "--input_size", type=int, + help="Override the input height and width, e.g. '380', default: keep original size") + args = parser.parse_args() + if not all([args.saved_model, args.onnx]): + parser.print_help() + print("\nThese arguments are required: --saved_model and --onnx") + sys.exit(1) + main(args) diff --git a/samples/python/efficientnet/eval_gt.py b/samples/python/efficientnet/eval_gt.py new file mode 100644 index 00000000..7f6c33a9 --- /dev/null +++ b/samples/python/efficientnet/eval_gt.py @@ -0,0 +1,78 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 os +import sys +import argparse + +import numpy as np + +from infer import TensorRTInfer +from image_batcher import ImageBatcher + + +def main(args): + annotations = {} + for line in open(args.annotations, "r"): + line = line.strip().split(args.separator) + if len(line) < 2 or not line[1].isnumeric(): + print("Could not parse the annotations file correctly, make sure the correct separator is used") + sys.exit(1) + annotations[os.path.basename(line[0])] = int(line[1]) + + trt_infer = TensorRTInfer(args.engine) + batcher = ImageBatcher(args.input, *trt_infer.input_spec(), preprocessor=args.preprocessor) + top1 = 0 + top5 = 0 + total = 0 + for batch, images in batcher.get_batch(): + classes, scores, top = trt_infer.infer(batch, top=5) + for i in range(len(images)): + image = os.path.basename(images[i]) + if image not in annotations.keys(): + print("Image '{}' does not appear in the annotations file, please make sure all evaluated " + "images have a corresponding ground truth label".format(image)) + sys.exit(1) + if annotations[image] == classes[i]: + top1 += 1 + if annotations[image] in top[0][i]: + top5 += 1 + total += 1 + top1_acc = 100 * (top1 / total) + top5_acc = 100 * (top5 / total) + print("Processing {} / {} : Top-1 {:0.1f}% , Top-5: {:0.1f}% ".format(total, batcher.num_images, + top1_acc, top5_acc), end="\r") + print() + print("Top-1 Accuracy: {:0.3f}%".format(top1_acc)) + print("Top-5 Accuracy: {:0.3f}%".format(top5_acc)) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-e", "--engine", help="The TensorRT engine to infer with") + parser.add_argument("-i", "--input", + help="The input to infer, either a single image path, or a directory of images") + parser.add_argument("-a", "--annotations", help="Set the file to use for classification ground truth annotations") + parser.add_argument("-s", "--separator", default=" ", + help="Separator to use between columns when parsing the annotations file, default: ' ' (space)") + parser.add_argument("-p", "--preprocessor", default="V2", choices=["V1", "V1MS", "V2"], + help="Select the image preprocessor to use, either 'V2', 'V1' or 'V1MS', default: V2") + args = parser.parse_args() + if not all([args.engine, args.input, args.annotations]): + parser.print_help() + print("\nThese arguments are required: --engine --input and --annotations") + sys.exit(1) + main(args) diff --git a/samples/python/efficientnet/image_batcher.py b/samples/python/efficientnet/image_batcher.py new file mode 100644 index 00000000..b9313e5a --- /dev/null +++ b/samples/python/efficientnet/image_batcher.py @@ -0,0 +1,176 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 os +import sys + +import numpy as np +from PIL import Image + + +class ImageBatcher: + """ + Creates batches of pre-processed images. + """ + + def __init__(self, input, shape, dtype, max_num_images=None, exact_batches=False, preprocessor="V2"): + """ + :param input: The input directory to read images from. + :param shape: The tensor shape of the batch to prepare, either in NCHW or NHWC format. + :param dtype: The (numpy) datatype to cast the batched data to. + :param max_num_images: The maximum number of images to read from the directory. + :param exact_batches: This defines how to handle a number of images that is not an exact multiple of the batch + size. If false, it will pad the final batch with zeros to reach the batch size. If true, it will *remove* the + last few images in excess of a batch size multiple, to guarantee batches are exact (useful for calibration). + :param preprocessor: Set the preprocessor to use, V1 or V2, depending on which network is being used. + """ + # Find images in the given input path + input = os.path.realpath(input) + self.images = [] + + extensions = [".jpg", ".jpeg", ".png", ".bmp"] + + def is_image(path): + return os.path.isfile(path) and os.path.splitext(path)[1].lower() in extensions + + if os.path.isdir(input): + self.images = [os.path.join(input, f) for f in os.listdir(input) if is_image(os.path.join(input, f))] + self.images.sort() + elif os.path.isfile(input): + if is_image(input): + self.images.append(input) + self.num_images = len(self.images) + if self.num_images < 1: + print("No valid {} images found in {}".format("/".join(extensions), input)) + sys.exit(1) + + # Handle Tensor Shape + self.dtype = dtype + self.shape = shape + assert len(self.shape) == 4 + self.batch_size = shape[0] + assert self.batch_size > 0 + self.format = None + self.width = -1 + self.height = -1 + if self.shape[1] == 3: + self.format = "NCHW" + self.height = self.shape[2] + self.width = self.shape[3] + elif self.shape[3] == 3: + self.format = "NHWC" + self.height = self.shape[1] + self.width = self.shape[2] + assert all([self.format, self.width > 0, self.height > 0]) + + # Adapt the number of images as needed + if max_num_images and 0 < max_num_images < len(self.images): + self.num_images = max_num_images + if exact_batches: + self.num_images = self.batch_size * (self.num_images // self.batch_size) + if self.num_images < 1: + print("Not enough images to create batches") + sys.exit(1) + self.images = self.images[0:self.num_images] + + # Subdivide the list of images into batches + self.num_batches = 1 + int((self.num_images - 1) / self.batch_size) + self.batches = [] + for i in range(self.num_batches): + start = i * self.batch_size + end = min(start + self.batch_size, self.num_images) + self.batches.append(self.images[start:end]) + + # Indices + self.image_index = 0 + self.batch_index = 0 + + self.preprocessor = preprocessor + + def preprocess_image(self, image_path): + """ + The image preprocessor loads an image from disk and prepares it as needed for batching. This includes cropping, + resizing, normalization, data type casting, and transposing. + This Image Batcher implements two algorithms: + * V2: The algorithm for EfficientNet V2, as defined in automl/efficientnetv2/preprocessing.py. + * V1: The algorithm for EfficientNet V1, aka "Legacy", as defined in automl/efficientnetv2/preprocess_legacy.py. + :param image_path: The path to the image on disk to load. + :return: A numpy array holding the image sample, ready to be contacatenated into the rest of the batch. + """ + + def pad_crop(image): + """ + A subroutine to implement padded cropping. This will create a center crop of the image, padded by 32 pixels. + :param image: The PIL image object + :return: The PIL image object already padded and cropped. + """ + # Assume square images + assert self.height == self.width + width, height = image.size + ratio = self.height / (self.height + 32) + crop_size = int(ratio * min(height, width)) + y = (height - crop_size) // 2 + x = (width - crop_size) // 2 + return image.crop((x, y, x + crop_size, y + crop_size)) + + image = Image.open(image_path) + image = image.convert(mode='RGB') + if self.preprocessor == "V2": + # For EfficientNet V2: Bilinear Resize and [-1,+1] Normalization + if self.height < 320: + # Padded crop only on smaller sizes + image = pad_crop(image) + image = image.resize((self.width, self.height), resample=Image.BILINEAR) + image = np.asarray(image, dtype=self.dtype) + image = (image - 128.0) / 128.0 + elif self.preprocessor == "V1": + # For EfficientNet V1: Padded Crop, Bicubic Resize, and [0,1] Normalization + # (Mean subtraction and Std Dev scaling will be part of the graph, so not done here) + image = pad_crop(image) + image = image.resize((self.width, self.height), resample=Image.BICUBIC) + image = np.asarray(image, dtype=self.dtype) + image = image / 255.0 + elif self.preprocessor == "V1MS": + # For EfficientNet V1: Padded Crop, Bicubic Resize, and [0,1] Normalization + # Mean subtraction and Std dev scaling are applied as a pre-processing step outside the graph. + image = pad_crop(image) + image = image.resize((self.width, self.height), resample=Image.BICUBIC) + image = np.asarray(image, dtype=self.dtype) + image = image - np.asarray([123.68, 116.28, 103.53]) + image = image / np.asarray([58.395, 57.120, 57.375]) + else: + print("Preprocessing method {} not supported".format(self.preprocessor)) + sys.exit(1) + if self.format == "NCHW": + image = np.transpose(image, (2, 0, 1)) + return image + + def get_batch(self): + """ + Retrieve the batches. This is a generator object, so you can use it within a loop as: + for batch, images in batcher.get_batch(): + ... + Or outside of a batch with the next() function. + :return: A generator yielding two items per iteration: a numpy array holding a batch of images, and the list of + paths to the images loaded within this batch. + """ + for i, batch_images in enumerate(self.batches): + batch_data = np.zeros(self.shape, dtype=self.dtype) + for i, image in enumerate(batch_images): + self.image_index += 1 + batch_data[i] = self.preprocess_image(image) + self.batch_index += 1 + yield batch_data, batch_images diff --git a/samples/python/efficientnet/infer.py b/samples/python/efficientnet/infer.py new file mode 100644 index 00000000..01c514be --- /dev/null +++ b/samples/python/efficientnet/infer.py @@ -0,0 +1,158 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 os +import sys +import argparse + +import numpy as np +import tensorrt as trt + +import pycuda.driver as cuda +import pycuda.autoinit + +from image_batcher import ImageBatcher + + +class TensorRTInfer: + """ + Implements inference for the EfficientNet TensorRT engine. + """ + + def __init__(self, engine_path): + """ + :param engine_path: The path to the serialized engine to load from disk. + """ + # Load TRT engine + self.logger = trt.Logger(trt.Logger.ERROR) + with open(engine_path, "rb") as f, trt.Runtime(self.logger) as runtime: + self.engine = runtime.deserialize_cuda_engine(f.read()) + self.context = self.engine.create_execution_context() + assert self.engine + assert self.context + + # Setup I/O bindings + self.inputs = [] + self.outputs = [] + self.allocations = [] + for i in range(self.engine.num_bindings): + is_input = False + if self.engine.binding_is_input(i): + is_input = True + name = self.engine.get_binding_name(i) + dtype = self.engine.get_binding_dtype(i) + shape = self.engine.get_binding_shape(i) + if is_input: + self.batch_size = shape[0] + size = np.dtype(trt.nptype(dtype)).itemsize + for s in shape: + size *= s + allocation = cuda.mem_alloc(size) + binding = { + 'index': i, + 'name': name, + 'dtype': np.dtype(trt.nptype(dtype)), + 'shape': list(shape), + 'allocation': allocation, + } + self.allocations.append(allocation) + if self.engine.binding_is_input(i): + self.inputs.append(binding) + else: + self.outputs.append(binding) + + assert self.batch_size > 0 + assert len(self.inputs) > 0 + assert len(self.outputs) > 0 + assert len(self.allocations) > 0 + + def input_spec(self): + """ + Get the specs for the input tensor of the network. Useful to prepare memory allocations. + :return: Two items, the shape of the input tensor and its (numpy) datatype. + """ + return self.inputs[0]['shape'], self.inputs[0]['dtype'] + + def output_spec(self): + """ + Get the specs for the output tensor of the network. Useful to prepare memory allocations. + :return: Two items, the shape of the output tensor and its (numpy) datatype. + """ + return self.outputs[0]['shape'], self.outputs[0]['dtype'] + + def infer(self, batch, top=1): + """ + Execute inference on a batch of images. The images should already be batched and preprocessed, as prepared by + the ImageBatcher class. Memory copying to and from the GPU device will be performed here. + :param batch: A numpy array holding the image batch. + :param top: The number of classes to return as top_predicitons, in descending order by their score. By default, + setting to one will return the same as the maximum score class. Useful for Top-5 accuracy metrics in validation. + :return: Three items, as numpy arrays for each batch image: The maximum score class, the corresponding maximum + score, and a list of the top N classes and scores. + """ + # Prepare the output data + output = np.zeros(*self.output_spec()) + + # Process I/O and execute the network + cuda.memcpy_htod(self.inputs[0]['allocation'], np.ascontiguousarray(batch)) + self.context.execute_v2(self.allocations) + cuda.memcpy_dtoh(output, self.outputs[0]['allocation']) + + # Process the results + classes = np.argmax(output, axis=1) + scores = np.max(output, axis=1) + top = min(top, output.shape[1]) + top_classes = np.flip(np.argsort(output, axis=1), axis=1)[:, 0:top] + top_scores = np.flip(np.sort(output, axis=1), axis=1)[:, 0:top] + + return classes, scores, [top_classes, top_scores] + + +def main(args): + trt_infer = TensorRTInfer(args.engine) + batcher = ImageBatcher(args.input, *trt_infer.input_spec(), preprocessor=args.preprocessor) + for batch, images in batcher.get_batch(): + classes, scores, top = trt_infer.infer(batch) + for i in range(len(images)): + if args.top == 1: + print(images[i], classes[i], scores[i], sep=args.separator) + else: + line = [images[i]] + assert args.top <= top[0].shape[1] + for t in range(args.top): + line.append(str(top[0][i][t])) + for t in range(args.top): + line.append(str(top[1][i][t])) + print(args.separator.join(line)) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-e", "--engine", help="The TensorRT engine to infer with") + parser.add_argument("-i", "--input", + help="The input to infer, either a single image path, or a directory of images") + parser.add_argument("-t", "--top", default=1, type=int, + help="The amount of top classes and scores to output per image, default: 1") + parser.add_argument("-s", "--separator", default="\t", + help="Separator to use between columns when printing the results, default: \\t") + parser.add_argument("-p", "--preprocessor", default="V2", choices=["V1", "V1MS", "V2"], + help="Select the image preprocessor to use, either 'V2', 'V1' or 'V1MS', default: V2") + args = parser.parse_args() + if not all([args.engine, args.input]): + parser.print_help() + print("\nThese arguments are required: --engine and --input") + sys.exit(1) + main(args) diff --git a/samples/python/efficientnet/requirements.txt b/samples/python/efficientnet/requirements.txt new file mode 100644 index 00000000..2e975e71 --- /dev/null +++ b/samples/python/efficientnet/requirements.txt @@ -0,0 +1,6 @@ +numpy>=1.19.4 +pycuda>=2020.1 +tensorrt>=7.1.0.0 +Pillow>=6.0.0 +onnx==1.8.1 +tf2onnx==1.8.1 \ No newline at end of file diff --git a/samples/python/end_to_end_tensorflow_mnist/README.md b/samples/python/end_to_end_tensorflow_mnist/README.md index 4aa42ed9..90d5bf34 100644 --- a/samples/python/end_to_end_tensorflow_mnist/README.md +++ b/samples/python/end_to_end_tensorflow_mnist/README.md @@ -48,11 +48,15 @@ def save(model, filename): 1. If running this sample in a test container, launch [NVIDIA tf1 (Tensorflow 1.x)](https://docs.nvidia.com/deeplearning/frameworks/tensorflow-release-notes/running.html#running) container in a separate terminal for generating the UFF model. ```bash - docker run --rm -it --gpus all -v `pwd`:/workspace nvcr.io/nvidia/tensorflow:20.12-tf1-py3 /bin/bash + docker run --rm -it --gpus all -v `pwd`:/workspace nvcr.io/nvidia/tensorflow:21.03-tf1-py3 /bin/bash ``` Alternatively, install Tensorflow 1.15 - `pip3 install tensorflow>=1.15.3,<2.0` + `pip3 install tensorflow>=1.15.5,<2.0` + + NOTE + - On PowerPC systems, you will need to manually install TensorFlow using IBM's [PowerAI](https://www.ibm.com/support/knowledgecenter/SS5SF7_1.6.0/navigation/pai_install.htm). + - On Jetson boards, you will need to manually install TensorFlow by following the documentation for [Xavier](https://docs.nvidia.com/deeplearning/dgx/install-tf-xavier/index.html) or [TX2](https://docs.nvidia.com/deeplearning/dgx/install-tf-jetsontx2/index.html). 2. Run the sample to train the model and write out the frozen graph: ```bash @@ -66,7 +70,9 @@ def save(model, filename): pip3 install --no-cache-dir --extra-index-url https://pypi.ngc.nvidia.com graphsurgeon ``` -4. Convert the `.pb` file to `.uff` using the convert-to-uff utility: +4. The MNIST dataset can be found under the data directory (usually `/usr/src/tensorrt/data/mnist`) if using the TensorRT containers. It is also bundled along with the [TensorRT tarball](https://developer.nvidia.com/nvidia-tensorrt-download). + +5. Convert the `.pb` file to `.uff` using the convert-to-uff utility: ```bash convert-to-uff models/lenet5.pb ``` @@ -80,25 +86,15 @@ def save(model, filename): python3 -m pip install -r requirements.txt ``` - NOTE: - - On PowerPC systems, you will need to manually install TensorFlow using IBM's [PowerAI](https://www.ibm.com/support/knowledgecenter/SS5SF7_1.6.0/navigation/pai_install.htm). - - On Jetson boards, you will need to manually install TensorFlow by following the documentation for [Xavier](https://docs.nvidia.com/deeplearning/dgx/install-tf-xavier/index.html) or [TX2](https://docs.nvidia.com/deeplearning/dgx/install-tf-jetsontx2/index.html). - -2. Download the MNIST test data. - ```bash - mkdir -p /usr/src/tensorrt/data/mnist - python3 ../scripts/download_mnist_pgms.py -o /usr/src/tensorrt/data/mnist - ``` - ## Running the sample 1. Create a TensorRT inference engine from the UFF file and run inference: ```bash - python sample.py [-d DATA_DIR] + python3 sample.py [-d DATA_DIR] ``` * NOTE: If the MNIST image data is not installed in the default location, `/usr/src/tensorrt/data/` as shown, the data directory must be specified. - For example: `python sample.py -d /path/to/my/data/`. + For example: `python3 sample.py -d /path/to/my/data/`. 2. Verify that the sample ran successfully. If the sample runs successfully you should see a match between the test case and the prediction. ``` diff --git a/samples/python/end_to_end_tensorflow_mnist/model.py b/samples/python/end_to_end_tensorflow_mnist/model.py index e45c636b..4e4d20ee 100644 --- a/samples/python/end_to_end_tensorflow_mnist/model.py +++ b/samples/python/end_to_end_tensorflow_mnist/model.py @@ -15,8 +15,9 @@ # # This file contains functions for training a TensorFlow model -import tensorflow as tf +import os import numpy as np +import tensorflow as tf def process_dataset(): # Import the data @@ -56,7 +57,9 @@ def main(): model.fit(x_train, y_train, epochs = 5, verbose = 1) # Evaluate the model on test data model.evaluate(x_test, y_test) - save(model, filename="models/lenet5.pb") + model_path = os.environ.get("MODEL_PATH") or os.path.join(os.path.dirname(__file__), "models") + model_file = os.path.join(model_path, "lenet5.pb") + save(model, filename=model_file) if __name__ == '__main__': main() diff --git a/samples/python/end_to_end_tensorflow_mnist/requirements.txt b/samples/python/end_to_end_tensorflow_mnist/requirements.txt index 01086f36..300526af 100644 --- a/samples/python/end_to_end_tensorflow_mnist/requirements.txt +++ b/samples/python/end_to_end_tensorflow_mnist/requirements.txt @@ -1,3 +1,3 @@ numpy Pillow>=8.1.2 -pycuda +pycuda<2021.1 diff --git a/samples/python/end_to_end_tensorflow_mnist/sample.py b/samples/python/end_to_end_tensorflow_mnist/sample.py index f8d1b1ac..74f81a5c 100644 --- a/samples/python/end_to_end_tensorflow_mnist/sample.py +++ b/samples/python/end_to_end_tensorflow_mnist/sample.py @@ -40,18 +40,19 @@ class ModelData(object): def build_engine(model_file): # For more information on TRT basics, refer to the introductory samples. - with trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network, builder.create_builder_config() as config, trt.UffParser() as parser: + with trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network, builder.create_builder_config() as config, trt.UffParser() as parser, trt.Runtime(TRT_LOGGER) as runtime: config.max_workspace_size = common.GiB(1) # Parse the Uff Network parser.register_input(ModelData.INPUT_NAME, ModelData.INPUT_SHAPE) parser.register_output(ModelData.OUTPUT_NAME) parser.parse(model_file, network) # Build and return an engine. - return builder.build_engine(network, config) + plan = builder.build_serialized_network(network, config) + return runtime.deserialize_cuda_engine(plan) # Loads a test case into the provided pagelocked_buffer. def load_normalized_test_case(data_paths, pagelocked_buffer, case_num=randint(0, 9)): - [test_case_path] = common.locate_files(data_paths, [str(case_num) + ".pgm"], err_msg="MNIST image data not found. Please follow the README instructions.") + [test_case_path] = common.locate_files(data_paths, [str(case_num) + ".pgm"], err_msg="Please follow the README in the mnist data directory (usually in `/usr/src/tensorrt/data/mnist`) to download the MNIST dataset") # Flatten the image into a 1D array, normalize, and copy to pagelocked memory. img = np.array(Image.open(test_case_path)).ravel() np.copyto(pagelocked_buffer, 1.0 - img / 255.0) diff --git a/samples/python/engine_refit_mnist/README.md b/samples/python/engine_refit_mnist/README.md index a84f5b9d..60c56722 100644 --- a/samples/python/engine_refit_mnist/README.md +++ b/samples/python/engine_refit_mnist/README.md @@ -40,19 +40,21 @@ The Pooling layer implements pooling within a channel. Supported pooling types a ## Prerequisites 1. Install the dependencies for Python. - ```bash - python3 -m pip install -r requirements.txt - ``` + `python3 -m pip install -r requirements.txt` - NOTE: - - On PowerPC systems, you will need to manually install PyTorch using IBM's [PowerAI](https://www.ibm.com/support/knowledgecenter/SS5SF7_1.6.0/navigation/pai_install.htm). +To run this sample you must be using Python 3.6 or newer. + +On PowerPC systems, you will need to manually install PyTorch using IBM's [PowerAI](https://www.ibm.com/support/knowledgecenter/SS5SF7_1.6.0/navigation/pai_install.htm). ## Running the sample 1. Run the sample to create a TensorRT engine and run inference: - ```bash - python3 sample.py - ``` + `python3 sample.py [-d DATA_DIR]` + + to run the sample with Python 3. + + **Note:** If the TensorRT sample data is not installed in the default location, for example `/usr/src/tensorrt/data/`, the data directory must be specified. For example: + `python sample.py -d /path/to/my/data/`. 2. Verify that the sample ran successfully. If the sample runs successfully you should see a match between the test case and the prediction after refitting. ``` @@ -96,9 +98,12 @@ For terms and conditions for use, reproduction, and distribution, see the [Tenso # Changelog +March 2021 +Documented the Python version limitations. + March 2019 This `README.md` file was recreated, updated and reviewed. # Known issues -There are no known issues in this sample. +This sample only supports Python 3.6+ due to `torch` and `torchvision` version requirements. diff --git a/samples/python/engine_refit_mnist/requirements.txt b/samples/python/engine_refit_mnist/requirements.txt index d023c351..14707111 100644 --- a/samples/python/engine_refit_mnist/requirements.txt +++ b/samples/python/engine_refit_mnist/requirements.txt @@ -1,6 +1,7 @@ numpy -f https://download.pytorch.org/whl/torch_stable.html -torch==1.5.0+cpu; platform_machine=="x86_64" and sys.platform=="linux" -torchvision==0.6.0; platform_machine=="x86_64" and sys.platform=="linux" +torch==1.8.1+cpu; platform_machine=="x86_64" and sys.platform=="linux" +torchvision==0.9.1; platform_machine=="x86_64" and sys.platform=="linux" Pillow>=8.1.2 -pycuda +pycuda<2021.1 +requests diff --git a/samples/python/engine_refit_mnist/sample.py b/samples/python/engine_refit_mnist/sample.py index d1d7497a..68501035 100644 --- a/samples/python/engine_refit_mnist/sample.py +++ b/samples/python/engine_refit_mnist/sample.py @@ -14,21 +14,20 @@ # limitations under the License. # + +import os +import sys + # This sample uses an MNIST PyTorch model to create a TensorRT Inference Engine -from PIL import Image import numpy as np - -import pycuda.driver as cuda import pycuda.autoinit - import tensorrt as trt -import sys, os sys.path.insert(1, os.path.join(sys.path[0], os.path.pardir)) -import common - import model +import common + # You can set the logger severity higher to suppress messages (or lower to display more messages). TRT_LOGGER = trt.Logger(trt.Logger.WARNING) @@ -52,6 +51,8 @@ def populate_network_with_some_dummy_weights(network, weights): conv1 = network.add_convolution(input=input_tensor, num_output_maps=20, kernel_shape=(5, 5), kernel=conv1_w, bias=conv1_b) conv1.name = "conv_1" conv1.stride = (1, 1) + # Associate weights with name and refit weights via name later in refitter. + network.set_weights_name(conv1_w, 'conv1.weight') pool1 = network.add_pooling(input=conv1.get_output(0), type=trt.PoolingType.MAX, window_size=(2, 2)) pool1.stride = (2, 2) @@ -80,14 +81,19 @@ def populate_network_with_some_dummy_weights(network, weights): # Build a TRT engine, but leave out some weights def build_engine_with_some_missing_weights(weights): # For more information on TRT basics, refer to the introductory samples. - with trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network: - builder.max_workspace_size = common.GiB(1) - # Set the refit flag in the builder - builder.refittable = True - # Populate the network using weights from the PyTorch model. - populate_network_with_some_dummy_weights(network, weights) - # Build and return an engine. - return builder.build_cuda_engine(network) + builder = trt.Builder(TRT_LOGGER) + network = builder.create_network() + config = builder.create_builder_config() + runtime = trt.Runtime(TRT_LOGGER) + + config.max_workspace_size = common.GiB(1) + # Set the refit flag in the builder + config.set_flag(trt.BuilderFlag.REFIT) + # Populate the network using weights from the PyTorch model. + populate_network_with_some_dummy_weights(network, weights) + # Build and return an engine. + plan = builder.build_serialized_network(network, config) + return runtime.deserialize_cuda_engine(plan) # Copy an image to the pagelocked input buffer def load_img_to_input_buffer(img, pagelocked_buffer): @@ -95,25 +101,26 @@ def load_img_to_input_buffer(img, pagelocked_buffer): # Get the accuracy on the test set using TensorRT def get_trt_test_accuracy(engine, inputs, outputs, bindings, stream, mnist_model): - with engine.create_execution_context() as context: - correct = 0 - total = 0 - # Run inference on every sample. - # Technically this could be batched, however this only comprises a fraction of total - # time spent in the test. - for test_img, test_name in mnist_model.get_all_test_samples(): - load_img_to_input_buffer(test_img, pagelocked_buffer=inputs[0].host) - # For more information on performing inference, refer to the introductory samples. - # The common.do_inference function will return a list of outputs - we only have one in this case. - [output] = common.do_inference(context, bindings=bindings, inputs=inputs, outputs=outputs, stream=stream) - pred = np.argmax(output) - correct += (test_name == pred) - total += 1 + context = engine.create_execution_context() + correct = 0 + total = 0 + # Run inference on every sample. + # Technically this could be batched, however this only comprises a fraction of total + # time spent in the test. + for test_img, test_name in mnist_model.get_all_test_samples(): + load_img_to_input_buffer(test_img, pagelocked_buffer=inputs[0].host) + # For more information on performing inference, refer to the introductory samples. + # The common.do_inference function will return a list of outputs - we only have one in this case. + [output] = common.do_inference(context, bindings=bindings, inputs=inputs, outputs=outputs, stream=stream) + pred = np.argmax(output) + correct += (test_name == pred) + total += 1 - accuracy = float(correct)/total - print("Got {} correct predictions out of {} ({:.1f}%)".format(correct, total, 100 * accuracy)) + accuracy = float(correct)/total + print("Got {} correct predictions out of {} ({:.1f}%)".format(correct, total, 100 * accuracy)) + + return accuracy - return accuracy def main(): common.add_help(description="Runs an MNIST network using a PyTorch model") @@ -122,35 +129,37 @@ def main(): mnist_model.learn() weights = mnist_model.get_weights() # Do inference with TensorRT. - with build_engine_with_some_missing_weights(weights) as engine: - # Build an engine, allocate buffers and create a stream. - # For more information on buffer allocation, refer to the introductory samples. - inputs, outputs, bindings, stream = common.allocate_buffers(engine) - print("Accuracy Before Engine Refit") - get_trt_test_accuracy(engine, inputs, outputs, bindings, stream, mnist_model) + engine = build_engine_with_some_missing_weights(weights) + # Build an engine, allocate buffers and create a stream. + # For more information on buffer allocation, refer to the introductory samples. + inputs, outputs, bindings, stream = common.allocate_buffers(engine) + print("Accuracy Before Engine Refit") + get_trt_test_accuracy(engine, inputs, outputs, bindings, stream, mnist_model) - # Refit the engine with the actual trained weights for the conv_1 layer. - with trt.Refitter(engine, TRT_LOGGER) as refitter: - # To get a list of all refittable layers and associated weightRoles - # in the network, use refitter.get_all() - # Set the actual weights for the conv_1 layer. Since it consists of - # kernel weights and bias weights, set each of them by specifying - # the WeightsRole. - refitter.set_weights("conv_1", trt.WeightsRole.KERNEL, - weights['conv1.weight'].numpy()) - refitter.set_weights("conv_1", trt.WeightsRole.BIAS, - weights['conv1.bias'].numpy()) - # Get description of missing weights. This should return empty - # lists in this case. - [missingLayers, weightRoles] = refitter.get_missing() - assert len(missingLayers) == 0, "Refitter found missing weights. Call set_weights() for all missing weights" - # Refit the engine with the new weights. This will return True if - # the refit operation succeeded. - assert refitter.refit_cuda_engine() + # Refit the engine with the actual trained weights for the conv_1 layer. + refitter = trt.Refitter(engine, TRT_LOGGER) - expected_correct_predictions = mnist_model.get_latest_test_set_accuracy() - print("Accuracy After Engine Refit (expecting {:.1f}% correct predictions)".format(100 * expected_correct_predictions)) - assert get_trt_test_accuracy(engine, inputs, outputs, bindings, stream, mnist_model) >= expected_correct_predictions + # To get a list of all refittable layers and associated weightRoles + # in the network, use refitter.get_all() + # Set the actual weights for the conv_1 layer. Since it consists of + # kernel weights and bias weights, set each of them by specifying + # the WeightsRole. + # Prefer to refit named weights via set_named_weights + refitter.set_named_weights('conv1.weight', weights['conv1.weight'].numpy()) + # set_named_weights is not available for unnamed weights. Call set_weights instead. + refitter.set_weights("conv_1", trt.WeightsRole.BIAS, + weights['conv1.bias'].numpy()) + # Get missing weights names. This should return empty + # lists in this case. + missing_weights = refitter.get_missing_weights() + assert len(missing_weights) == 0, "Refitter found missing weights. Call set_named_weights() or set_weights() for all missing weights" + # Refit the engine with the new weights. This will return True if + # the refit operation succeeded. + assert refitter.refit_cuda_engine() + + expected_correct_predictions = mnist_model.get_latest_test_set_accuracy() + print("Accuracy After Engine Refit (expecting {:.1f}% correct predictions)".format(100 * expected_correct_predictions)) + assert get_trt_test_accuracy(engine, inputs, outputs, bindings, stream, mnist_model) >= expected_correct_predictions if __name__ == '__main__': main() diff --git a/samples/python/engine_refit_onnx_bidaf/README.md b/samples/python/engine_refit_onnx_bidaf/README.md new file mode 100644 index 00000000..c1b6a72f --- /dev/null +++ b/samples/python/engine_refit_onnx_bidaf/README.md @@ -0,0 +1,118 @@ +# TensorRT Engine Refitting of ONNX models. + +**Table Of Contents** +- [Description](#description) +- [How does this sample work?](#how-does-this-sample-work) +- [Prerequisites](#prerequisites) +- [Running the sample](#running-the-sample) +- [Additional resources](#additional-resources) +- [License](#license) +- [Changelog](#changelog) +- [Known issues](#known-issues) + +## Description + +This sample shows how to refit an engine built from an ONNX model via parsers. A modified version of the [ONNX BiDAF model](https://github.com/onnx/models/tree/master/text/machine_comprehension/bidirectional_attention_flow) is used as the sample model, which implements the Bi-Directional Attention Flow (BiDAF) network described in the paper [Bidirectional Attention Flow for Machine Comprehension](https://arxiv.org/abs/1611.01603). + +## How does this sample work? + +This sample replaces unsupported nodes (HardMax / Compress) in the original ONNX model via ONNX-graphsurgeon (in `prepare_model.py`) and build a refittable TensorRT engine. +The engine is then refitted with fake weights and correct weights, each followed by inference on sample context and query sentences in `build_and_refit_engine.py`. + +## Prerequisites + +Dependencies required for this sample + +1. Install the dependencies for Python: +`python3 -m pip install -r requirements.txt` + +2. TensorRT + +3. [ONNX-GraphSurgeon](https://github.com/NVIDIA/TensorRT/tree/master/tools/onnx-graphsurgeon) + +4. Download sample data. See the "Download Sample Data" section of [the general setup guide](../README.md). + +## Running the sample + +* Prepare the ONNX model. (The data directory needs to be specified.) + ```sh + python3 prepare_model.py + ``` + +The output should look similar to the following: +``` +Modifying the ONNX model ... +Modified ONNX model saved as bidaf-modified.onnx +Done. +``` + +The script will modify the original model from [onnx/models](https://github.com/onnx/models/raw/c02f8c8699fc12273649e658b8d2a1a8e32a35d0/text/machine_comprehension/bidirectional_attention_flow/model/bidaf-9.onnx) and save an ONNX model that can be parsed and run by TensorRT. + +The original ONNX model contains four CategoryMapper nodes to map the four input string arrays to int arrays. +Since TensorRT does not support string data type and CategoryMapper nodes, we dump out the four maps for the four nodes as json files (`model/CategoryMapper_{4-6}.json`) and use them to preprocess input data. +Now the four inputs become four outputs of the original CategoryMapper nodes. + +And unsupported HardMax nodes and Compress nodes are replaced by ArgMax nodes and Gather nodes, respectively. + + +* Build a TensorRT engine, refit the engine and run inference. +`python3 build_and_refit_engine.py` + +The script will build a TensorRT engine from the modified ONNX model, and then refit the engine and run inference on sample context and query sentences. + +When running the above command for the first time, the output should look similar to the following: +``` +Loading ONNX file from path bidaf-modified.onnx... +Beginning ONNX file parsing +[TensorRT] WARNING: onnx2trt_utils.cpp:283: Your ONNX model has been generated with INT64 weights, while TensorRT does not natively support INT64. Attempting to cast down to INT32. +[TensorRT] WARNING: Tensor DataType is determined at build time for tensors not marked as input or output. +[TensorRT] WARNING: Tensor DataType is determined at build time for tensors not marked as input or output. +Completed parsing of ONNX file +Network inputs: +CategoryMapper_4 (-1, 1) +CategoryMapper_5 (-1, 1, 1, 16) +CategoryMapper_6 (-1, 1) +CategoryMapper_7 (-1, 1, 1, 16) +Building an engine from file bidaf-modified.onnx; this may take a while... +Completed creating Engine +Refitting engine... +Doing inference... +Refitting engine... +Doing inference... +Passed +``` + +When running the above command again, engine will be deserialized from the plan file, the output should look similar to the following: +``` +Reading engine from file bidaf.trt +Refitting engine... +Doing inference... +Refitting engine... +Doing inference... +Passed +``` + +# Additional resources + +The following resources provide a deeper understanding about the model used in this sample: + +**Model** +- [Bidirectional Attention Flow for Machine Comprehension](https://arxiv.org/abs/1611.01603) + +**Documentation** +- [Introduction To NVIDIA’s TensorRT Samples](https://docs.nvidia.com/deeplearning/sdk/tensorrt-sample-support-guide/index.html#samples) +- [Working With TensorRT Using The Python API](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#python_topics) +- [Importing A Model Using A Parser In Python](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#import_model_python) +- [NVIDIA’s TensorRT Documentation Library](https://docs.nvidia.com/deeplearning/sdk/tensorrt-archived/index.html) + +# 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 2020: This sample was recreated, updated and reviewed. + +# Known issues + +There are no known issues in this sample. diff --git a/samples/python/engine_refit_onnx_bidaf/build_and_refit_engine.py b/samples/python/engine_refit_onnx_bidaf/build_and_refit_engine.py new file mode 100644 index 00000000..cbf58ecd --- /dev/null +++ b/samples/python/engine_refit_onnx_bidaf/build_and_refit_engine.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 os +import sys + +import numpy as np +import pycuda.autoinit +import tensorrt as trt +from data_processing import get_inputs, preprocess + +sys.path.insert(1, os.path.join(sys.path[0], "..")) +import common + +TRT_LOGGER = trt.Logger() + + +def get_engine(onnx_file_path, engine_file_path): + """Attempts to load a serialized engine if available, otherwise builds a new TensorRT engine and saves it.""" + def build_engine(): + """Takes an ONNX file and creates a TensorRT engine to run inference with""" + builder = trt.Builder(TRT_LOGGER) + network = builder.create_network(common.EXPLICIT_BATCH) + parser = trt.OnnxParser(network, TRT_LOGGER) + runtime = trt.Runtime(TRT_LOGGER) + + # Parse model file + print('Loading ONNX file from path {}...'.format(onnx_file_path)) + with open(onnx_file_path, 'rb') as model: + print('Beginning ONNX file parsing') + if not parser.parse(model.read()): + print('ERROR: Failed to parse the ONNX file.') + for error in range(parser.num_errors): + print(parser.get_error(error)) + return None + print('Completed parsing of ONNX file') + + # Print input info + print('Network inputs:') + for i in range(network.num_inputs): + tensor = network.get_input(i) + print(tensor.name, trt.nptype(tensor.dtype), tensor.shape) + + network.get_input(0).shape = [10, 1] + network.get_input(1).shape = [10, 1, 1, 16] + network.get_input(2).shape = [6, 1] + network.get_input(3).shape = [6, 1, 1, 16] + + config = builder.create_builder_config() + config.set_flag(trt.BuilderFlag.REFIT) + config.max_workspace_size = 1 << 28 # 256MiB + + print('Building an engine from file {}; this may take a while...'.format( + onnx_file_path)) + plan = builder.build_serialized_network(network, config) + engine = runtime.deserialize_cuda_engine(plan) + print("Completed creating Engine") + + with open(engine_file_path, "wb") as f: + f.write(plan) + return engine + + if os.path.exists(engine_file_path): + # If a serialized engine exists, use it instead of building an engine. + print("Reading engine from file {}".format(engine_file_path)) + with open(engine_file_path, "rb") as f: + runtime = trt.Runtime(TRT_LOGGER) + return runtime.deserialize_cuda_engine(f.read()) + else: + return build_engine() + + +def main(): + onnx_file_path = 'bidaf-modified.onnx' + engine_file_path = "bidaf.trt" + + # input + context = 'A quick brown fox jumps over the lazy dog.' + query = 'What color is the fox?' + cw_str, _ = preprocess(context) + # get ravelled data + cw, cc, qw, qc = get_inputs(context, query) + + # Do inference with TensorRT + refit_weights = np.load("Parameter576_B_0.npy") + fake_weights = np.ones_like(refit_weights) + engine = get_engine(onnx_file_path, engine_file_path) + refitter = trt.Refitter(engine, TRT_LOGGER) + context = engine.create_execution_context() + + for weights, answer_correct in [(fake_weights, False), (refit_weights, True)]: + print("Refitting engine...") + # To get a list of all refittable weights' names + # in the network, use refitter.get_all_weights(). + + # Refit named weights via set_named_weights + refitter.set_named_weights('Parameter576_B_0', weights) + # Get missing weights names. This should return empty + # lists in this case. + missing_weights = refitter.get_missing_weights() + assert len( + missing_weights) == 0, "Refitter found missing weights. Call set_named_weights() or set_weights() for all missing weights" + # Refit the engine with the new weights. This will return True if + # the refit operation succeeded. + assert refitter.refit_cuda_engine() + + inputs, outputs, bindings, stream = common.allocate_buffers(engine) + print("Doing inference...") + # Do inference + # Set host input. The common.do_inference_v2 function will copy the input to the GPU before executing. + inputs[0].host = cw + inputs[1].host = cc + inputs[2].host = qw + inputs[3].host = qc + trt_outputs = common.do_inference_v2(context, bindings=bindings, inputs=inputs, outputs=outputs, stream=stream) + + start = np.asscalar(trt_outputs[0]) + end = np.asscalar(trt_outputs[1]) + answer = [w.encode() for w in cw_str[start:end + 1].reshape(-1)] + assert answer_correct == (answer == [b'brown']) + print("Passed") + + +if __name__ == '__main__': + main() diff --git a/samples/python/engine_refit_onnx_bidaf/data_processing.py b/samples/python/engine_refit_onnx_bidaf/data_processing.py new file mode 100644 index 00000000..1a49fa25 --- /dev/null +++ b/samples/python/engine_refit_onnx_bidaf/data_processing.py @@ -0,0 +1,59 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 numpy as np +import nltk +from nltk import word_tokenize +import json +import tensorrt as trt + +def preprocess(text): + try: + nltk.data.find('tokenizers/punkt') + except LookupError: + nltk.download('punkt') + tokens = word_tokenize(text) + # split into lower-case word tokens, in numpy array with shape of (seq, 1) + words = np.asarray([w.lower() for w in tokens]).reshape(-1, 1) + # split words into chars, in numpy array with shape of (seq, 1, 1, 16) + chars = [[c for c in t][:16] for t in tokens] + chars = [cs+['']*(16-len(cs)) for cs in chars] + chars = np.asarray(chars).reshape(-1, 1, 1, 16) + return words, chars + +def get_map_func(filepath): + file = open(filepath) + category_map = json.load(file) + category_mapper = dict(zip(category_map["cats_strings"], category_map["cats_int64s"])) + default_int64 = category_map["default_int64"] + func = lambda s: category_mapper.get(s, default_int64) + return np.vectorize(func) + + +def get_inputs(context, query): + cw, cc = preprocess(context) + qw, qc = preprocess(query) + + context_word_func = get_map_func("CategoryMapper_4.json") + context_char_func = get_map_func("CategoryMapper_5.json") + query_word_func = get_map_func("CategoryMapper_6.json") + query_char_func = get_map_func("CategoryMapper_7.json") + + cw_input = context_word_func(cw).astype(trt.nptype(trt.int32)).ravel() + cc_input = context_char_func(cc).astype(trt.nptype(trt.int32)).ravel() + qw_input = query_word_func(qw).astype(trt.nptype(trt.int32)).ravel() + qc_input = query_char_func(qc).astype(trt.nptype(trt.int32)).ravel() + return cw_input, cc_input, qw_input, qc_input diff --git a/samples/python/engine_refit_onnx_bidaf/download.yml b/samples/python/engine_refit_onnx_bidaf/download.yml new file mode 100644 index 00000000..0b8fd896 --- /dev/null +++ b/samples/python/engine_refit_onnx_bidaf/download.yml @@ -0,0 +1,5 @@ +sample: engine_refit_onnx_bidaf +files: + - path: samples/python/engine_refit_onnx_bidaf/bidaf-original.onnx + url: https://github.com/onnx/models/raw/c02f8c8699fc12273649e658b8d2a1a8e32a35d0/text/machine_comprehension/bidirectional_attention_flow/model/bidaf-9.onnx + checksum: cf11f1eceb4731f8dd39345467fe94a1 diff --git a/samples/python/engine_refit_onnx_bidaf/prepare_model.py b/samples/python/engine_refit_onnx_bidaf/prepare_model.py new file mode 100644 index 00000000..f9b81f50 --- /dev/null +++ b/samples/python/engine_refit_onnx_bidaf/prepare_model.py @@ -0,0 +1,98 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 onnx +import numpy as np +import json + +import sys, os +sys.path.insert(1, os.path.join(sys.path[0], "..")) +from downloader import getFilePath + +def drop_category_mapper_nodes(graph): + new_inputs = [] + for org_input in graph.inputs: + # head node, simply disconnect it with others + assert len(org_input.outputs) == 1 + category_mapper_node = org_input.outputs[0] + assert category_mapper_node.op == 'CategoryMapper' + assert len(category_mapper_node.outputs) == 1 + new_inputs.append(category_mapper_node.outputs[0]) + category_mapper_node.inputs.clear() + category_mapper_node.outputs.clear() + + # Save mapping info to preprocess inputs. + with open(category_mapper_node.name + '.json', 'w') as fp: + json.dump(category_mapper_node.attrs, fp) + + graph.inputs = new_inputs + +def replace_unsupported_ops(graph): + # replace hardmax with ArgMax + hardmaxes = [node for node in graph.nodes if node.op == "Hardmax"] + assert len(hardmaxes) == 1 + hardmax = hardmaxes[0] + hardmax.op = "ArgMax" + hardmax.name = "ArgMax(org:" + hardmax.name + ")" + hardmax.attrs["axis"] = 1 + hardmax.attrs["keepdims"] = 0 + + cast = hardmax.o() + reshape = cast.o() + + hardmax.outputs = reshape.outputs + assert len(hardmax.outputs) == 1 + hardmax.outputs[0].dtype = np.int64 + hardmax.outputs[0].shape = [1] + + compress = reshape.o() + compress.op = "Gather" + compress.name = "Gather(org:" + compress.name + ")" + compress.attrs["axis"] = 1 + + cast.outputs.clear() + reshape.outputs.clear() + # Remove the node from the graph completely + graph.cleanup().toposort() + +def save_weights_for_refitting(graph): + # Save weights for refitting + tmap = graph.tensors() + np.save("Parameter576_B_0.npy", tmap["Parameter576_B_0"].values) + + +def main(): + org_model_file_path = getFilePath('samples/python/engine_refit_onnx_bidaf/bidaf-original.onnx') + + print("Modifying the ONNX model ...") + original_model = onnx.load(org_model_file_path) + graph = gs.import_onnx(original_model) + + drop_category_mapper_nodes(graph) + replace_unsupported_ops(graph) + save_weights_for_refitting(graph) + + new_model = gs.export_onnx(graph) + + modified_model_name = "bidaf-modified.onnx" + onnx.checker.check_model(new_model) + onnx.save(new_model, modified_model_name) + print("Modified ONNX model saved as {}".format(modified_model_name)) + print("Done.") + +if __name__ == '__main__': + main() diff --git a/samples/python/engine_refit_onnx_bidaf/requirements.txt b/samples/python/engine_refit_onnx_bidaf/requirements.txt new file mode 100644 index 00000000..bd9bb8d5 --- /dev/null +++ b/samples/python/engine_refit_onnx_bidaf/requirements.txt @@ -0,0 +1,4 @@ +numpy>=1.15.1 +pycuda<2021.1 +nltk>=3.5 +wget diff --git a/samples/python/int8_caffe_mnist/README.md b/samples/python/int8_caffe_mnist/README.md index 2e6f29a4..fe5b567b 100644 --- a/samples/python/int8_caffe_mnist/README.md +++ b/samples/python/int8_caffe_mnist/README.md @@ -14,7 +14,7 @@ ## Description -This sample, `int8_caffe_mnist`, demonstrates how to create an INT8 calibrator, build and calibrate an engine for INT8 mode, and finally run inference in INT8 mode. +This sample, int8_caffe_mnist, demonstrates how to create an INT8 calibrator, build and calibrate an engine for INT8 mode, and finally run inference in INT8 mode. ## How does this sample work? @@ -25,24 +25,18 @@ During inference, the sample loads a random batch from the calibrator, then perf ## Prerequisites 1. Install the dependencies for Python. - ```bash - python3 -m pip install -r requirements.txt - ``` + `python3 -m pip install -r requirements.txt` -2. Download the [MNIST dataset](http://yann.lecun.com/exdb/mnist/) - - This sample requires the [training set](http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz), [test set](http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz) and [test labels](http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz). +2. The MNIST dataset can be found under the data directory (usually `/usr/src/tensorrt/data/mnist`) if using the TensorRT containers. It is also bundled along with the [TensorRT tarball](https://developer.nvidia.com/nvidia-tensorrt-download). + - This sample requires the [training set](http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz), [test set](http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz) and [test labels](http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz). - - Download and unzip the MNIST data: - ```bash - sh ../scripts/download_mnist_data.sh - ``` ## Running the sample 1. Run the sample to create a TensorRT inference engine, perform IN8 calibration and run inference: - ```bash - python3 sample.py [-d DATA_DIR] - ``` + `python3 sample.py [-d DATA_DIR]` + + to run the sample with Python 3. **Note:** If the TensorRT sample data is not installed in the default location, for example `/usr/src/tensorrt/data/`, the `data` directory must be specified. For example: `python sample.py -d /path/to/my/data/`. diff --git a/samples/python/int8_caffe_mnist/requirements.txt b/samples/python/int8_caffe_mnist/requirements.txt index 01086f36..300526af 100644 --- a/samples/python/int8_caffe_mnist/requirements.txt +++ b/samples/python/int8_caffe_mnist/requirements.txt @@ -1,3 +1,3 @@ numpy Pillow>=8.1.2 -pycuda +pycuda<2021.1 diff --git a/samples/python/int8_caffe_mnist/sample.py b/samples/python/int8_caffe_mnist/sample.py index bb1abb38..a32471cb 100644 --- a/samples/python/int8_caffe_mnist/sample.py +++ b/samples/python/int8_caffe_mnist/sample.py @@ -42,7 +42,7 @@ class ModelData(object): # This function builds an engine from a Caffe model. def build_int8_engine(deploy_file, model_file, calib, batch_size=32): - with trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network, builder.create_builder_config() as config, trt.CaffeParser() as parser: + with trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network, builder.create_builder_config() as config, trt.CaffeParser() as parser, trt.Runtime(TRT_LOGGER) as runtime: # We set the builder batch size to be the same as the calibrator's, as we use the same batches # during inference. Note that this is not required in general, and inference batch size is # independent of calibration batch size. @@ -54,7 +54,8 @@ def build_int8_engine(deploy_file, model_file, calib, batch_size=32): model_tensors = parser.parse(deploy=deploy_file, model=model_file, network=network, dtype=ModelData.DTYPE) network.mark_output(model_tensors.find(ModelData.OUTPUT_NAME)) # Build engine and do int8 calibration. - return builder.build_engine(network, config) + plan = builder.build_serialized_network(network, config) + return runtime.deserialize_cuda_engine(plan) def check_accuracy(context, batch_size, test_set, test_labels): diff --git a/samples/python/introductory_parser_samples/README.md b/samples/python/introductory_parser_samples/README.md index d135267b..ac9013a2 100644 --- a/samples/python/introductory_parser_samples/README.md +++ b/samples/python/introductory_parser_samples/README.md @@ -17,43 +17,39 @@ ## Description -This sample, `introductory_parser_samples`, is a Python sample which uses TensorRT and its included suite of parsers (the UFF, Caffe and ONNX parsers), to perform inference with ResNet-50 models trained with various different frameworks. +This sample, introductory_parser_samples, is a Python sample which uses TensorRT and its included suite of parsers (the UFF, Caffe and ONNX parsers), to perform inference with ResNet-50 models trained with various different frameworks. ## How does this sample work? This sample is a collection of three smaller samples, with each focusing on a specific parser. The following sections describe how each sample works. -### Caffe Resnet50 +### caffe_resnet50 This sample demonstrates how to build an engine from a trained Caffe model using the Caffe parser and then run inference. The Caffe parser is used for Caffe2 models. After training, you can invoke the Caffe parser directly on the model file (usually `.caffemodel`) and deploy file (usually `.prototxt`). -### ONNX Resnet50 +### onnx_resnet50 This sample demonstrates how to build an engine from an ONNX model file using the open-source ONNX parser and then run inference. The ONNX parser can be used with any framework that supports the ONNX format (typically `.onnx` files). -### UFF Resnet50 +### uff_resnet50 This sample demonstrates how to build an engine from a UFF model file (converted from a TensorFlow protobuf) and then run inference. The UFF parser is used for TensorFlow models. After freezing a TensorFlow graph and writing it to a protobuf file, you can convert it to UFF with the `convert-to-uff` utility included with TensorRT. This sample ships with a pre-generated UFF file. ## Prerequisites 1. Install the dependencies for Python. - ```bash - python3 -m pip install -r requirements.txt - ``` - -2. Download and untar the sample data from the [TensorRT release tarball](https://developer.nvidia.com/nvidia-tensorrt-download#) to the default location `/usr/src/tensorrt/data` - * **NOTE:** This step can be skipped when using [NVIDIA TensorRT container](https://docs.nvidia.com/deeplearning/tensorrt/container-release-notes/running.html#running) to run the sample as it has the data premounted. + `python3 -m pip install -r requirements.txt` ## Running the sample 1. Run the sample to create a TensorRT inference engine and run inference: - ```bash - python3 _resnet50.py - ``` - * Where `` is either `caffe`, `onnx`, or `uff`. + `python _resnet50.py` + + Where `` is either `caffe`, `onnx`, or `uff`. + + **Note:** If the TensorRT sample data is not installed in the default location, for example `/usr/src/tensorrt/data/`, the `data` directory must be specified. + `python _resnet50.py [-d DATA_DIR]` - * **NOTE:** If the TensorRT sample data is not installed in the default location, `/usr/src/tensorrt/data/`, the `data` directory must be specified. For example: `python caffe_resnet50.py -d /path/to/my/data/` 2. Verify that the sample ran successfully. If the sample runs successfully you should see output similar to the following: diff --git a/samples/python/introductory_parser_samples/onnx_resnet50.py b/samples/python/introductory_parser_samples/onnx_resnet50.py index d0e78312..b52ce8ff 100644 --- a/samples/python/introductory_parser_samples/onnx_resnet50.py +++ b/samples/python/introductory_parser_samples/onnx_resnet50.py @@ -14,21 +14,21 @@ # limitations under the License. # +import os # This sample uses an ONNX ResNet50 Model to create a TensorRT Inference Engine import random -from PIL import Image -import numpy as np +import sys -import pycuda.driver as cuda +import numpy as np # This import causes pycuda to automatically manage CUDA context creation and cleanup. import pycuda.autoinit - import tensorrt as trt +from PIL import Image -import sys, os sys.path.insert(1, os.path.join(sys.path[0], "..")) import common + class ModelData(object): MODEL_PATH = "ResNet50.onnx" INPUT_SHAPE = (3, 224, 224) @@ -40,16 +40,21 @@ TRT_LOGGER = trt.Logger(trt.Logger.WARNING) # The Onnx path is used for Onnx models. def build_engine_onnx(model_file): - with trt.Builder(TRT_LOGGER) as builder, builder.create_network(common.EXPLICIT_BATCH) as network, builder.create_builder_config() as config, trt.OnnxParser(network, TRT_LOGGER) as parser: - config.max_workspace_size = common.GiB(1) - # Load the Onnx model and parse it in order to populate the TensorRT network. - with open(model_file, 'rb') as model: - if not parser.parse(model.read()): - print ('ERROR: Failed to parse the ONNX file.') - for error in range(parser.num_errors): - print (parser.get_error(error)) - return None - return builder.build_engine(network, config) + builder = trt.Builder(TRT_LOGGER) + network = builder.create_network(common.EXPLICIT_BATCH) + config = builder.create_builder_config() + parser = trt.OnnxParser(network, TRT_LOGGER) + + config.max_workspace_size = common.GiB(1) + # Load the Onnx model and parse it in order to populate the TensorRT network. + with open(model_file, 'rb') as model: + if not parser.parse(model.read()): + print ('ERROR: Failed to parse the ONNX file.') + for error in range(parser.num_errors): + print (parser.get_error(error)) + return None + return builder.build_engine(network, config) + def load_normalized_test_case(test_image, pagelocked_buffer): # Converts the input image to a CHW Numpy array @@ -64,6 +69,7 @@ def load_normalized_test_case(test_image, pagelocked_buffer): np.copyto(pagelocked_buffer, normalize_image(Image.open(test_image))) return test_image + def main(): # Set the data path to the directory that contains the trained models and test images for inference. _, data_files = common.find_sample_data(description="Runs a ResNet50 network with a TensorRT inference engine.", subfolder="resnet50", find_files=["binoculars.jpeg", "reflex_camera.jpeg", "tabby_tiger_cat.jpg", ModelData.MODEL_PATH, "class_labels.txt"]) @@ -73,24 +79,26 @@ def main(): labels = open(labels_file, 'r').read().split('\n') # Build a TensorRT engine. - with build_engine_onnx(onnx_model_file) as engine: - # Inference is the same regardless of which parser is used to build the engine, since the model architecture is the same. - # Allocate buffers and create a CUDA stream. - inputs, outputs, bindings, stream = common.allocate_buffers(engine) - # Contexts are used to perform inference. - with engine.create_execution_context() as context: - # Load a normalized test case into the host input page-locked buffer. - test_image = random.choice(test_images) - test_case = load_normalized_test_case(test_image, inputs[0].host) - # Run the engine. The output will be a 1D tensor of length 1000, where each value represents the - # probability that the image corresponds to that label - trt_outputs = common.do_inference_v2(context, bindings=bindings, inputs=inputs, outputs=outputs, stream=stream) - # We use the highest probability as our prediction. Its index corresponds to the predicted label. - pred = labels[np.argmax(trt_outputs[0])] - if "_".join(pred.split()) in os.path.splitext(os.path.basename(test_case))[0]: - print("Correctly recognized " + test_case + " as " + pred) - else: - print("Incorrectly recognized " + test_case + " as " + pred) + engine = build_engine_onnx(onnx_model_file) + # Inference is the same regardless of which parser is used to build the engine, since the model architecture is the same. + # Allocate buffers and create a CUDA stream. + inputs, outputs, bindings, stream = common.allocate_buffers(engine) + # Contexts are used to perform inference. + context = engine.create_execution_context() + + # Load a normalized test case into the host input page-locked buffer. + test_image = random.choice(test_images) + test_case = load_normalized_test_case(test_image, inputs[0].host) + # Run the engine. The output will be a 1D tensor of length 1000, where each value represents the + # probability that the image corresponds to that label + trt_outputs = common.do_inference_v2(context, bindings=bindings, inputs=inputs, outputs=outputs, stream=stream) + # We use the highest probability as our prediction. Its index corresponds to the predicted label. + pred = labels[np.argmax(trt_outputs[0])] + if "_".join(pred.split()) in os.path.splitext(os.path.basename(test_case))[0]: + print("Correctly recognized " + test_case + " as " + pred) + else: + print("Incorrectly recognized " + test_case + " as " + pred) + if __name__ == '__main__': main() diff --git a/samples/python/introductory_parser_samples/requirements.txt b/samples/python/introductory_parser_samples/requirements.txt index 01086f36..300526af 100644 --- a/samples/python/introductory_parser_samples/requirements.txt +++ b/samples/python/introductory_parser_samples/requirements.txt @@ -1,3 +1,3 @@ numpy Pillow>=8.1.2 -pycuda +pycuda<2021.1 diff --git a/samples/python/network_api_pytorch_mnist/README.md b/samples/python/network_api_pytorch_mnist/README.md index 6340c3c5..cb7500b8 100644 --- a/samples/python/network_api_pytorch_mnist/README.md +++ b/samples/python/network_api_pytorch_mnist/README.md @@ -1,13 +1,13 @@ # “Hello World” For TensorRT Using PyTorch And Python - **Table Of Contents** + - [Description](#description) - [How does this sample work?](#how-does-this-sample-work) - * [TensorRT API layers and ops](#tensorrt-api-layers-and-ops) + * [TensorRT API layers and ops](#tensorrt-api-layers-and-ops) - [Prerequisites](#prerequisites) - [Running the sample](#running-the-sample) - * [Sample `--help` options](#sample-help-options) + * [Sample `--help` options](#sample-help-options) - [Additional resources](#additional-resources) - [License](#license) - [Changelog](#changelog) @@ -15,7 +15,7 @@ ## Description -This sample, `network_api_pytorch_mnist`, trains a convolutional model on the [MNIST](http://yann.lecun.com/exdb/mnist/) dataset and runs inference with a TensorRT engine. +This sample, network_api_pytorch_mnist, trains a convolutional model on the [MNIST](http://yann.lecun.com/exdb/mnist/) dataset and runs inference with a TensorRT engine. ## How does this sample work? @@ -42,22 +42,24 @@ The Pooling layer implements pooling within a channel. Supported pooling types a ## Prerequisites 1. Install the dependencies for Python. - `python3 -m pip install -r requirements.txt` + `python3 -m pip install -r requirements.txt` - * NOTE: On PowerPC systems, you will need to manually install PyTorch using IBM's [PowerAI](https://www.ibm.com/support/knowledgecenter/SS5SF7_1.6.0/navigation/pai_install.htm). +To run this sample you must be using Python 3.6 or newer. + +On PowerPC systems, you will need to manually install PyTorch using IBM's [PowerAI](https://www.ibm.com/support/knowledgecenter/SS5SF7_1.6.0/navigation/pai_install.htm). + +2. The MNIST dataset can be found under the data directory (usually `/usr/src/tensorrt/data/mnist`) if using the TensorRT containers. It is also bundled along with the [TensorRT tarball](https://developer.nvidia.com/nvidia-tensorrt-download). ## Running the sample 1. Run the sample to create a TensorRT inference engine and run inference: - ```bash - python sample.py - ``` + `python sample.py` 2. Verify that the sample ran successfully. If the sample runs successfully you should see a match between the test case and the prediction. - ``` - Test Case: 0 - Prediction: 0 - ``` + ``` + Test Case: 0 + Prediction: 0 + ``` ### Sample --help options @@ -82,13 +84,14 @@ The following resources provide a deeper understanding about getting started wit 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 +March 2021 +Documented the Python version limitations. + February 2019 This `README.md` file was recreated, updated and reviewed. - # Known issues -There are no known issues in this sample. +This sample only supports Python 3.6+ due to `torch` and `torchvision` version requirements. diff --git a/samples/python/network_api_pytorch_mnist/requirements.txt b/samples/python/network_api_pytorch_mnist/requirements.txt index d023c351..14707111 100644 --- a/samples/python/network_api_pytorch_mnist/requirements.txt +++ b/samples/python/network_api_pytorch_mnist/requirements.txt @@ -1,6 +1,7 @@ numpy -f https://download.pytorch.org/whl/torch_stable.html -torch==1.5.0+cpu; platform_machine=="x86_64" and sys.platform=="linux" -torchvision==0.6.0; platform_machine=="x86_64" and sys.platform=="linux" +torch==1.8.1+cpu; platform_machine=="x86_64" and sys.platform=="linux" +torchvision==0.9.1; platform_machine=="x86_64" and sys.platform=="linux" Pillow>=8.1.2 -pycuda +pycuda<2021.1 +requests diff --git a/samples/python/network_api_pytorch_mnist/sample.py b/samples/python/network_api_pytorch_mnist/sample.py index ebcec08c..2ee02274 100644 --- a/samples/python/network_api_pytorch_mnist/sample.py +++ b/samples/python/network_api_pytorch_mnist/sample.py @@ -14,17 +14,15 @@ # limitations under the License. # +import os +import sys + # This sample uses an MNIST PyTorch model to create a TensorRT Inference Engine import model -from PIL import Image import numpy as np - -import pycuda.driver as cuda import pycuda.autoinit - import tensorrt as trt -import sys, os sys.path.insert(1, os.path.join(sys.path[0], "..")) import common @@ -71,14 +69,20 @@ def populate_network(network, weights): fc2.get_output(0).name = ModelData.OUTPUT_NAME network.mark_output(tensor=fc2.get_output(0)) + def build_engine(weights): # For more information on TRT basics, refer to the introductory samples. - with trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network, builder.create_builder_config() as config: - config.max_workspace_size = common.GiB(1) - # Populate the network using weights from the PyTorch model. - populate_network(network, weights) - # Build and return an engine. - return builder.build_engine(network, config) + builder = trt.Builder(TRT_LOGGER) + network = builder.create_network() + config = builder.create_builder_config() + runtime = trt.Runtime(TRT_LOGGER) + + config.max_workspace_size = common.GiB(1) + # Populate the network using weights from the PyTorch model. + populate_network(network, weights) + # Build and return an engine. + plan = builder.build_serialized_network(network, config) + return runtime.deserialize_cuda_engine(plan) # Loads a random test case from pytorch's DataLoader def load_random_test_case(model, pagelocked_buffer): @@ -95,18 +99,20 @@ def main(): mnist_model.learn() weights = mnist_model.get_weights() # Do inference with TensorRT. - with build_engine(weights) as engine: - # Build an engine, allocate buffers and create a stream. - # For more information on buffer allocation, refer to the introductory samples. - inputs, outputs, bindings, stream = common.allocate_buffers(engine) - with engine.create_execution_context() as context: - case_num = load_random_test_case(mnist_model, pagelocked_buffer=inputs[0].host) - # For more information on performing inference, refer to the introductory samples. - # The common.do_inference function will return a list of outputs - we only have one in this case. - [output] = common.do_inference(context, bindings=bindings, inputs=inputs, outputs=outputs, stream=stream) - pred = np.argmax(output) - print("Test Case: " + str(case_num)) - print("Prediction: " + str(pred)) + engine = build_engine(weights) + + # Build an engine, allocate buffers and create a stream. + # For more information on buffer allocation, refer to the introductory samples. + inputs, outputs, bindings, stream = common.allocate_buffers(engine) + context = engine.create_execution_context() + + case_num = load_random_test_case(mnist_model, pagelocked_buffer=inputs[0].host) + # For more information on performing inference, refer to the introductory samples. + # The common.do_inference function will return a list of outputs - we only have one in this case. + [output] = common.do_inference(context, bindings=bindings, inputs=inputs, outputs=outputs, stream=stream) + pred = np.argmax(output) + print("Test Case: " + str(case_num)) + print("Prediction: " + str(pred)) if __name__ == '__main__': main() diff --git a/samples/python/onnx_packnet/README.md b/samples/python/onnx_packnet/README.md index 1e88649f..4c1dc6ef 100644 --- a/samples/python/onnx_packnet/README.md +++ b/samples/python/onnx_packnet/README.md @@ -32,52 +32,62 @@ This sample converts the Pytorch graph into ONNX and uses ONNX-parser included i Dependencies required for this sample -1. Install the dependencies for Python: - ```bash - python3 -m pip install -r requirements.txt - ``` +1. Install the dependencies for Python: `python3 -m pip install -r requirements.txt` - * NOTE: The included scripts do not currently work with Torch 1.5.0. Thus, it is recommended that you use 1.4.0, which is the default version in the requirements file. + NOTE: The included scripts do not currently work with Torch 1.5.0. Thus, it is recommended that you use 1.4.0, which is the default version in the requirements file. + +2. [ONNX-GraphSurgeon](https://github.com/NVIDIA/TensorRT/tree/master/tools/onnx-graphsurgeon) -2. Install [ONNX-GraphSurgeon](https://github.com/NVIDIA/TensorRT/tree/master/tools/onnx-graphsurgeon) - ```bash - pip3 install --no-cache-dir --extra-index-url https://pypi.ngc.nvidia.com onnx-graphsurgeon - ``` ## Running the sample -### Cloning the packnet repository +### Preparing packnet -Clone the packnet repository and set `PYTHONPATH` variable accordingly. +[Packnet](https://github.com/TRI-ML/packnet-sfm) can be either downloaded or cloned. - ```bash - git clone https://github.com/TRI-ML/packnet-sfm.git packnet-sfm --depth 1 --branch v0.1.2 - export PYTHONPATH=$PYTHONPATH:$PWD/packnet-sfm - ``` +**Download** + +Download the source (see the "Download Sample Data" section of [the general setup guide](../README.md)) +, unzip the downloaded file and setup `PYTHONPATH`. + +``` +unzip $TRT_DATA_DIR/samples/python/onnx_packnet/packnet-sfm-0.1.2.zip -d $PWD +export PYTHONPATH=$PWD/packnet-sfm-0.1.2 +``` + +**Clone** + +Clone the [packnet](https://github.com/TRI-ML/packnet-sfm) repository and set `PYTHONPATH` variable accordingly. + +``` +git clone https://github.com/TRI-ML/packnet-sfm.git packnet-sfm +pushd packnet-sfm && git checkout tags/v0.1.2 && popd +export PYTHONPATH=$PWD/packnet-sfm +``` ### Conversion to ONNX Run the following command to convert the Packnet pytorch network to ONNX graph. This step also includes handling custom layers (Group Normalization) and using ONNX-GS to modify upsample and pad layers. - ```bash - python3 convert_to_onnx.py --output model.onnx - ``` +``` +python3 convert_to_onnx.py --output model.onnx +``` ### Inference with TensorRT Once the ONNX graph is generated, use `trtexec` tool (located in `bin` directory of TensorRT package) to perform inference on a random input image. - ```bash - trtexec --onnx=model.onnx --explicitBatch - ``` +``` +trtexec --onnx=model.onnx --explicitBatch +``` -Please refer to [trtexec documentation](https://github.com/NVIDIA/TensorRT/tree/master/samples/opensource/trtexec) for detailed usage options. +Please refer to `trtexec` tool for more commandline options. ### Sample --help options To see the full list of available options and their descriptions, use the `-h` or `--help` command line option. For example: - ```bash - convert_to_onnx.py -h - ``` +``` +convert_to_onnx.py -h +``` # Additional resources diff --git a/samples/python/onnx_packnet/download.yml b/samples/python/onnx_packnet/download.yml index 87ce6707..9763aee1 100644 --- a/samples/python/onnx_packnet/download.yml +++ b/samples/python/onnx_packnet/download.yml @@ -1,5 +1,5 @@ sample: onnx_packnet files: - - path: packnet-sfm.zip + - path: samples/python/onnx_packnet/packnet-sfm-0.1.2.zip url: https://github.com/TRI-ML/packnet-sfm/archive/v0.1.2.zip checksum: 7a73db591d3955ccf407910cd928d9c0 diff --git a/samples/python/onnx_packnet/requirements.txt b/samples/python/onnx_packnet/requirements.txt index 992af8c2..ce1f39c9 100644 --- a/samples/python/onnx_packnet/requirements.txt +++ b/samples/python/onnx_packnet/requirements.txt @@ -1,5 +1,6 @@ -f https://download.pytorch.org/whl/torch_stable.html +torchvision==0.5.0; platform_machine=="x86_64" and sys.platform=="linux" torch==1.4.0+cpu; platform_machine=="x86_64" and sys.platform=="linux" -onnx==1.7.0 +onnx==1.8.0 numpy -pycuda +pycuda<2021.1 diff --git a/samples/python/uff_custom_plugin/README.md b/samples/python/uff_custom_plugin/README.md index 7a51f50c..96116bcd 100644 --- a/samples/python/uff_custom_plugin/README.md +++ b/samples/python/uff_custom_plugin/README.md @@ -39,7 +39,7 @@ The ClipPlugin headers. This script trains an MNIST network. `model.py` -This script trains an MNIST network that uses ReLU6 activation using the clip plugin. +This script converts the MNIST tensorflow model to UFF that replaces ReLU6 activation with the clip plugin. `sample.py` This script transforms the trained model into UFF (delegating ReLU6 activations to ClipPlugin instances) and runs inference in TensorRT. @@ -51,11 +51,15 @@ This file specifies all the Python packages required to run this Python sample. 1. If running this sample in a test container, launch [NVIDIA tf1 (Tensorflow 1.x)](https://docs.nvidia.com/deeplearning/frameworks/tensorflow-release-notes/running.html#running) container in a separate terminal for generating the UFF model. ```bash - docker run --rm -it --gpus all -v `pwd`:/workspace nvcr.io/nvidia/tensorflow:20.12-tf1-py3 /bin/bash + docker run --rm -it --gpus all -v `pwd`:/workspace nvcr.io/nvidia/tensorflow:21.03-tf1-py3 /bin/bash ``` Alternatively, install Tensorflow 1.15 - `pip3 install tensorflow>=1.15.3,<2.0` + `pip3 install tensorflow>=1.15.5,<2.0` + + NOTE + - On PowerPC systems, you will need to manually install TensorFlow using IBM's [PowerAI](https://www.ibm.com/support/knowledgecenter/SS5SF7_1.6.0/navigation/pai_install.htm). + - On Jetson boards, you will need to manually install TensorFlow by following the documentation for [Xavier](https://docs.nvidia.com/deeplearning/dgx/install-tf-xavier/index.html) or [TX2](https://docs.nvidia.com/deeplearning/dgx/install-tf-jetsontx2/index.html). 2. Install the UFF toolkit and graph surgeon depending on your [TensorRT installation method](https://docs.nvidia.com/deeplearning/sdk/tensorrt-install-guide/index.html#installing), or from PyPI: ```bash @@ -63,27 +67,22 @@ This file specifies all the Python packages required to run this Python sample. pip3 install --no-cache-dir --extra-index-url https://pypi.ngc.nvidia.com graphsurgeon ``` -3. Run these scripts to train the model, covert to UFF and save the model: +3. Run the sample to train the model, covert to UFF and save the model. Also save the test data: ```bash mkdir -p models - python lenet5.py - python model.py + python3 lenet5.py + python3 model.py ``` ## Prerequisites -1. [Install CMake](https://cmake.org/download/). +For specific software versions, see the [TensorRT Installation Guide](https://docs.nvidia.com/deeplearning/sdk/tensorrt-archived/index.html). -2. Switch back to test container (if applicable) and install the dependencies for Python. +1. Switch back to test container (if applicable) and install the dependencies for Python. ```bash python3 -m pip install -r requirements.txt ``` - - NOTE - - On PowerPC systems, you will need to manually install TensorFlow using IBM's [PowerAI](https://www.ibm.com/support/knowledgecenter/SS5SF7_1.6.0/navigation/pai_install.htm). - - On Jetson boards, you will need to manually install TensorFlow by following the documentation for [Xavier](https://docs.nvidia.com/deeplearning/dgx/install-tf-xavier/index.html) or [TX2](https://docs.nvidia.com/deeplearning/dgx/install-tf-jetsontx2/index.html). - -3. Install the UFF toolkit and graph surgeon; depending on your TensorRT installation method, to install the toolkit and graph surgeon, choose the method you used to install TensorRT for instructions (see [TensorRT Installation Guide: Installing TensorRT](https://docs.nvidia.com/deeplearning/sdk/tensorrt-install-guide/index.html#installing)). +2. [Install CMake](https://cmake.org/download/). ## Running the sample @@ -96,8 +95,7 @@ This file specifies all the Python packages required to run this Python sample. **NOTE:** If any of the dependencies are not installed in their default locations, you can manually specify them. For example: ``` - cmake .. -DPYBIND11_DIR=/path/to/pybind11/ - -DCMAKE_CUDA_COMPILER=/usr/local/cuda-x.x/bin/nvcc (Or adding /path/to/nvcc into $PATH) + cmake .. -DCMAKE_CUDA_COMPILER=/usr/local/cuda-x.x/bin/nvcc (Or adding /path/to/nvcc into $PATH) -DCUDA_INC_DIR=/usr/local/cuda-x.x/include/ (Or adding /path/to/cuda/include into $CPLUS_INCLUDE_PATH) -DPYTHON3_INC_DIR=/usr/include/python3.6/ -DTRT_LIB=/path/to/tensorrt/lib/ diff --git a/samples/python/uff_custom_plugin/plugin/clipKernel.cu b/samples/python/uff_custom_plugin/plugin/clipKernel.cu index 13b316c1..3fe5f17f 100644 --- a/samples/python/uff_custom_plugin/plugin/clipKernel.cu +++ b/samples/python/uff_custom_plugin/plugin/clipKernel.cu @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,7 @@ * limitations under the License. */ + #include template diff --git a/samples/python/uff_custom_plugin/plugin/clipKernel.h b/samples/python/uff_custom_plugin/plugin/clipKernel.h index 89f70f62..73dc11d3 100644 --- a/samples/python/uff_custom_plugin/plugin/clipKernel.h +++ b/samples/python/uff_custom_plugin/plugin/clipKernel.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,12 @@ #define CLIP_KERNEL_H #include "NvInfer.h" -int clipInference(cudaStream_t stream, int n, float clipMin, float clipMax, const void* input, void* output); +int clipInference( + cudaStream_t stream, + int n, + float clipMin, + float clipMax, + const void* input, + void* output); #endif diff --git a/samples/python/uff_custom_plugin/plugin/customClipPlugin.cpp b/samples/python/uff_custom_plugin/plugin/customClipPlugin.cpp index 1204f01f..e3e1abc9 100644 --- a/samples/python/uff_custom_plugin/plugin/customClipPlugin.cpp +++ b/samples/python/uff_custom_plugin/plugin/customClipPlugin.cpp @@ -104,7 +104,7 @@ int ClipPlugin::initialize() noexcept return 0; } -int ClipPlugin::enqueue(int batchSize, const void* const* inputs, void** outputs, void*, cudaStream_t stream) noexcept +int ClipPlugin::enqueue(int batchSize, const void* const* inputs, void* const* outputs, void*, cudaStream_t stream) noexcept { int status = -1; diff --git a/samples/python/uff_custom_plugin/plugin/customClipPlugin.h b/samples/python/uff_custom_plugin/plugin/customClipPlugin.h index 24cfc6df..46c84ef3 100644 --- a/samples/python/uff_custom_plugin/plugin/customClipPlugin.h +++ b/samples/python/uff_custom_plugin/plugin/customClipPlugin.h @@ -50,7 +50,7 @@ public: return 0; }; - int enqueue(int batchSize, const void* const* inputs, void** outputs, void* workspace, + int enqueue(int batchSize, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept override; size_t getSerializationSize() const noexcept override; diff --git a/samples/python/uff_custom_plugin/requirements.txt b/samples/python/uff_custom_plugin/requirements.txt index 8a1a8f96..dcd15f08 100644 --- a/samples/python/uff_custom_plugin/requirements.txt +++ b/samples/python/uff_custom_plugin/requirements.txt @@ -1,3 +1,3 @@ Pillow>=8.1.2 -pycuda +pycuda<2021.1 numpy diff --git a/samples/python/uff_ssd/README.md b/samples/python/uff_ssd/README.md index 7410e699..329dab64 100644 --- a/samples/python/uff_ssd/README.md +++ b/samples/python/uff_ssd/README.md @@ -24,7 +24,7 @@ This sample is based on the TensorFlow implementation of SSD. For more informati ## How does this sample work? -The sample downloads a pretrained [ssd_inception_v2_coco_2017_11_17](http://download.tensorflow.org/models/object_detection/ssd_inception_v2_coco_2017_11_17.tar.gz) model and uses it to perform inference. Additionally, it superimposes bounding boxes on the input image as a post-processing step. +The sample uses a pretrained [ssd_inception_v2_coco_2017_11_17](http://download.tensorflow.org/models/object_detection/ssd_inception_v2_coco_2017_11_17.tar.gz) model to perform inference. Additionally, it superimposes bounding boxes on the input image as a post-processing step. The SSD network performs the task of object detection and localization in a single forward pass of the network. The TensorFlow SSD network was trained on the InceptionV2 architecture using the [MSCOCO dataset](http://cocodataset.org/#home). @@ -137,69 +137,99 @@ The outputs of the SSD network are human interpretable. The post-processing work 1. Launch the [NVIDIA tf1 (Tensorflow 1.x)](https://docs.nvidia.com/deeplearning/frameworks/tensorflow-release-notes/running.html#running) container. ```bash - docker run --rm -it --gpus all -v `pwd`:/workspace nvcr.io/nvidia/tensorflow:20.12-tf1-py3 /bin/bash + docker run --rm -it --gpus all -v `pwd`:/workspace nvcr.io/nvidia/tensorflow:21.03-tf1-py3 /bin/bash ``` Alternatively, install Tensorflow 1.15 - `pip3 install tensorflow>=1.15.3,<2.0` + `pip3 install tensorflow>=1.15.5,<2.0` + + NOTE: + - On PowerPC systems, you will need to manually install TensorFlow using IBM's [PowerAI](https://www.ibm.com/support/knowledgecenter/SS5SF7_1.6.0/navigation/pai_install.htm). + - On Jetson boards, you will need to manually install TensorFlow by following the documentation for [Xavier](https://docs.nvidia.com/deeplearning/dgx/install-tf-xavier/index.html) or [TX2](https://docs.nvidia.com/deeplearning/dgx/install-tf-jetsontx2/index.html). 2. Install the dependencies for Python. ```bash python3 -m pip install -r requirements.txt ``` - NOTE: - - On PowerPC systems, you will need to manually install TensorFlow using IBM's [PowerAI](https://www.ibm.com/support/knowledgecenter/SS5SF7_1.6.0/navigation/pai_install.htm). - - On Jetson boards, you will need to manually install TensorFlow by following the documentation for [Xavier](https://docs.nvidia.com/deeplearning/dgx/install-tf-xavier/index.html) or [TX2](https://docs.nvidia.com/deeplearning/dgx/install-tf-jetsontx2/index.html). +3. Download the Tensorflow SSD model with inception backbone. + ```bash + wget http://download.tensorflow.org/models/object_detection/ssd_inception_v2_coco_2017_11_17.tar.gz + ``` -2. Optional: To evaluate the accuracy of the trained model using the VOC dataset, perform the following steps. +4. Convert Tensorflow model to UFF. + ```bash + python3 model.py -d $PWD + ``` - Download the VOC 2007 dataset. Run the following command from the sample root directory. +Optional: To evaluate the accuracy of the trained model using the VOC dataset, perform the following steps. + +5. Download the VOC 2007 dataset. Run the following command from the sample root directory. ```bash wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar - tar xvf VOCtest_06-Nov-2007.tar ``` The first command downloads the VOC dataset from the Oxford servers, and the second command unpacks the dataset. - **NOTE:** If the download link is broken, try alternate source http://vision.cs.utexas.edu/voc/VOC2007_test/. If you don’t want to save VOC in the sample root directory, you'll need to adjust the `--voc_dir` argument to `voc_evaluation.py` script before running it. The default value of this argument is `/VOCdevkit/VOC2007`. + **NOTE:** If the download link is broken, try alternate source http://vision.cs.utexas.edu/voc/VOC2007_test/. If you don’t want to save VOC in the sample root directory, you'll need to adjust the `--voc_dir` argument to `voc_evaluation.py` script before running it. The default value of this argument is `$PWD/VOCdevkit/VOC2007`. + +6. Run the VOC evaluation script for tensorflow. + + ```bash + python3 voc_evaluation.py tensorflow -d $PWD + ``` ## Running the sample Both the `detect_objects.py` and `voc_evaluation.py` scripts support separate advanced features, for example, lower precision inference, changing workspace directory and changing batch size. -1. Run the inference script: +1. Return to the test container, install prerequisites and run the TensorRT inference script: ```bash + python3 -m pip install -r requirements.txt python3 detect_objects.py ``` - Where `` contains the image you want to run inference on using the SSD network. The script should work for all popular image formats, like PNG, JPEG, and BMP. Since the model is trained for images of size 300 x 300, the input image will be resized to this size (using bilinear interpolation), if needed. + Where `` contains the image you want to run inference on using the SSD network. The script should work for all popular image formats, like PNG, JPEG, and BMP. Since the model is trained for images of size 300 x 300, the input image will be resized to this size (using bilinear interpolation), if needed. - For example: + Example #1: + ```bash + python3 detect_objects.py images/image1.jpg + ``` + + Expected output: + ``` + TensorRT inference engine settings: + * Inference precision - DataType.FLOAT + * Max batch size - 1 + + Loading cached TensorRT engine from workspace/engines/FLOAT/engine_bs_1.buf + TensorRT inference time: 309 ms + Detected dog with confidence 98% + Detected dog with confidence 93% + Detected person with confidence 75% + Total time taken for one image: 338 ms + + Saved output image to: image_inferred.jpg + ``` + + Example #2: ```bash wget -nc http://images.cocodataset.org/val2017/000000252219.jpg -O test.jpg python3 detect_objects.py test.jpg ``` - When the inference script is run for the first time, it will run the following things to prepare its workspace: - - The script downloads the pretrained `ssd_inception_v2_coco_2017_11_17` model from the TensorFlow object detection API. The script converts this model to TensorRT format, and the conversion is tailored to this specific version of the model. - - The script builds a TensorRT inference engine and saves it to a file. During this step, all TensorRT optimizations will be applied to frozen graph. This is a time consuming operation and it can take a few minutes. + When the inference script is run for the first time, the script builds a TensorRT inference engine and saves it to a file. During this step, all TensorRT optimizations will be applied to frozen graph. This is a time consuming operation and it can take a few minutes. - After the workspace is ready, the script launches inference on the input image and saves the results to a location that will be printed on standard output. You can then open the saved image file and visually confirm that the bounding boxes are correct. + After the workspace is ready, the script launches inference on the input image and saves the results to a location that will be printed on standard output. You can then open the saved image file and visually confirm that the bounding boxes are correct. -2. Run the VOC evaluation script. +Optional: To evaluate the accuracy of the trained model using the VOC dataset, perform the following steps. - 1. Run the script using TensorRT: +2. Run the VOC evaluation script for TensorRT. ```bash - python3 voc_evaluation.py - ``` - - 2. Run the script using TensorFlow: - ```bash - python3 voc_evaluation.py tensorflow + python3 voc_evaluation.py -d $PWD ``` **NOTE:** Running the script using TensorFlow will much slower than the TensorRT evaluation. - 3. AP and mAP metrics are displayed at the end of the script execution. The metrics for the TensorRT engine should match those of the original TensorFlow model. +3. AP and mAP metrics are displayed at the end of the script execution. The metrics for the TensorRT engine should match those of the original TensorFlow model. ### Sample --help options diff --git a/samples/python/uff_ssd/detect_objects.py b/samples/python/uff_ssd/detect_objects.py index 9c156972..8995e4f3 100755 --- a/samples/python/uff_ssd/detect_objects.py +++ b/samples/python/uff_ssd/detect_objects.py @@ -15,22 +15,18 @@ # limitations under the License. # -import os -import ctypes -import time -import sys import argparse +import os +import time import numpy as np -from PIL import Image +import pycuda.autoinit import tensorrt as trt - -import utils.inference as inference_utils # TRT/TF inference wrappers -import utils.model as model_utils # UFF conversion -import utils.boxes as boxes_utils # Drawing bounding boxes -import utils.coco as coco_utils # COCO dataset descriptors -from utils.paths import PATHS # Path management - +import utils.boxes as boxes_utils # Drawing bounding boxes +import utils.coco as coco_utils # COCO dataset descriptors +from utils.inference_trt import TRTInference # TRT inference wrappers +from PIL import Image +from utils.paths import PATHS # Path management # COCO label list COCO_LABELS = coco_utils.COCO_CLASSES_LIST @@ -118,9 +114,15 @@ def parse_commandline_arguments(): parser.add_argument("-o", "--output", help="path of the output file", default=os.path.join(PATHS.get_sample_root(), "image_inferred.jpg")) + parser.add_argument('-d', '--data', + help="Specify the data directory where it is saved in. $TRT_DATA_DIR will be overwritten by this argument.") - # Parse arguments passed - args = parser.parse_args() + args, _ = parser.parse_known_args() + + data_dir = os.environ.get('TRT_DATA_DIR', None) if args.data is None else args.data + if data_dir is None: + raise ValueError("Data directory must be specified by either `-d $DATA` or environment variable $TRT_DATA_DIR.") + PATHS.set_data_dir_path(data_dir) # Set workspace dir path if passed by user if args.workspace_dir: @@ -149,14 +151,10 @@ def main(): # Parse command line arguments args = parse_commandline_arguments() - # Fetch .uff model path, convert from .pb - # if needed, using prepare_ssd_model + # Fetch .uff model path ssd_model_uff_path = PATHS.get_model_uff_path(MODEL_NAME) - if not os.path.exists(ssd_model_uff_path): - model_utils.prepare_ssd_model(MODEL_NAME) - # Set up all TensorRT data structures needed for inference - trt_inference_wrapper = inference_utils.TRTInference( + trt_inference_wrapper = TRTInference( args.trt_engine_path, ssd_model_uff_path, trt_engine_datatype=args.trt_engine_datatype, batch_size=args.max_batch_size) diff --git a/samples/python/uff_ssd/model.py b/samples/python/uff_ssd/model.py new file mode 100644 index 00000000..f4568877 --- /dev/null +++ b/samples/python/uff_ssd/model.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 os +import utils.model as model_utils # UFF conversion +from utils.paths import PATHS # Path management + +# Model used for inference +MODEL_NAME = 'ssd_inception_v2_coco_2017_11_17' + +def parse_commandline_arguments(): + """Parses command line arguments and adjusts internal data structures.""" + + # Define script command line arguments + parser = argparse.ArgumentParser(description='Run object detection inference on input image.') + parser.add_argument('-w', '--workspace_dir', + help='sample workspace directory') + parser.add_argument('-d', '--data', + help="Specify the data directory where it is saved in. $TRT_DATA_DIR will be overwritten by this argument.") + + args, _ = parser.parse_known_args() + + data_dir = os.environ.get('TRT_DATA_DIR', None) if args.data is None else args.data + if data_dir is None: + raise ValueError("Data directory must be specified by either `-d $DATA` or environment variable $TRT_DATA_DIR.") + PATHS.set_data_dir_path(data_dir) + + # Set workspace dir path if passed by user + if args.workspace_dir: + PATHS.set_workspace_dir_path(args.workspace_dir) + + try: + os.makedirs(PATHS.get_workspace_dir_path()) + except: + pass + + # Verify Paths after adjustments. This also exits script if verification fails + PATHS.verify_all_paths() + + return args + +def main(): + # Parse command line arguments + args = parse_commandline_arguments() + + # Fetch .uff model path + ssd_model_uff_path = PATHS.get_model_uff_path(MODEL_NAME) + # convert from .pb if needed, using prepare_ssd_model + if not os.path.exists(ssd_model_uff_path): + model_utils.prepare_ssd_model(MODEL_NAME) + +if __name__ == '__main__': + main() diff --git a/samples/python/uff_ssd/requirements.txt b/samples/python/uff_ssd/requirements.txt index d5bc895f..89cdebdc 100644 --- a/samples/python/uff_ssd/requirements.txt +++ b/samples/python/uff_ssd/requirements.txt @@ -1,4 +1,4 @@ numpy Pillow>=8.1.2 -pycuda +pycuda<2021.1 requests diff --git a/samples/python/uff_ssd/utils/engine.py b/samples/python/uff_ssd/utils/engine.py index 2b51ec7b..54131eef 100644 --- a/samples/python/uff_ssd/utils/engine.py +++ b/samples/python/uff_ssd/utils/engine.py @@ -22,7 +22,7 @@ import tensorrt as trt import pycuda.driver as cuda import numpy as np -from utils.model import ModelData +from utils.modeldata import ModelData # ../../common.py sys.path.insert(1, @@ -80,7 +80,7 @@ def allocate_buffers(engine): return inputs, outputs, bindings, stream def build_engine(uff_model_path, trt_logger, trt_engine_datatype=trt.DataType.FLOAT, batch_size=1, silent=False): - with trt.Builder(trt_logger) as builder, builder.create_network() as network, builder.create_builder_config() as config, trt.UffParser() as parser: + with trt.Builder(trt_logger) as builder, builder.create_network() as network, builder.create_builder_config() as config, trt.UffParser() as parser, trt.Runtime(trt_logger) as runtime: config.max_workspace_size = 1 << 30 if trt_engine_datatype == trt.DataType.HALF: config.set_flag(trt.BuilderFlag.FP16) @@ -93,7 +93,8 @@ def build_engine(uff_model_path, trt_logger, trt_engine_datatype=trt.DataType.FL if not silent: print("Building TensorRT engine. This may take few minutes.") - return builder.build_engine(network, config) + plan = builder.build_serialized_network(network, config) + return runtime.deserialize_cuda_engine(plan) def save_engine(engine, engine_dest_path): buf = engine.serialize() diff --git a/samples/python/uff_ssd/utils/inference_tf.py b/samples/python/uff_ssd/utils/inference_tf.py new file mode 100644 index 00000000..9e63437d --- /dev/null +++ b/samples/python/uff_ssd/utils/inference_tf.py @@ -0,0 +1,83 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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 tensorflow as tf +from PIL import Image +import numpy as np + +import utils.model as model_utils # UFF conversion uttils + +# This class is similar as TRTInference inference, but it manages Tensorflow +class TensorflowInference(object): + def __init__(self, pb_model_path): + self.detection_graph = tf.Graph() + with self.detection_graph.as_default(): + od_graph_def = tf.GraphDef() + with tf.gfile.GFile(pb_model_path, 'rb') as fid: + serialized_graph = fid.read() + od_graph_def.ParseFromString(serialized_graph) + tf.import_graph_def(od_graph_def, name='') + self.sess = tf.Session(graph=self.detection_graph) + + def infer(self, image_path): + img_np = self._load_img(image_path) + return self._run_tensorflow_graph(np.expand_dims(img_np, axis=0)) + + def infer_batch(self, image_paths): + img_np = self._load_imgs(image_paths) + return self._run_tensorflow_graph(img_np) + + def _run_tensorflow_graph(self, image_input): + ops = self.detection_graph.get_operations() + all_tensor_names = {output.name for op in ops for output in op.outputs} + tensor_dict = {} + for key in [ + 'num_detections', 'detection_boxes', + 'detection_scores', 'detection_classes' + ]: + tensor_name = key + ':0' + if tensor_name in all_tensor_names: + tensor_dict[key] = self.detection_graph.get_tensor_by_name( + tensor_name) + + image_tensor = self.detection_graph.get_tensor_by_name('image_tensor:0') + output_dict = self.sess.run(tensor_dict, + feed_dict={image_tensor: image_input}) + + # All outputs are float32 numpy arrays, so convert types as appropriate + output_dict['num_detections'] = output_dict['num_detections'].astype(np.int32) + output_dict['detection_classes'] = output_dict[ + 'detection_classes'].astype(np.uint8) + + return output_dict + + def _load_image_into_numpy_array(self, image): + (im_width, im_height) = image.size + return np.array(image).reshape( + (im_height, im_width, model_utils.ModelData.get_input_channels()) + ).astype(np.uint8) + + def _load_imgs(self, image_paths): + numpy_array = np.zeros((len(image_paths),) + (300, 300, 3)) + for idx, image_path in enumerate(image_paths): + img_np = self._load_img(image_path) + numpy_array[idx] = img_np + return numpy_array + + def _load_img(self, image_path): + img = Image.open(image_path) + img_np = self._load_image_into_numpy_array(img) + return img_np diff --git a/samples/python/uff_ssd/utils/inference.py b/samples/python/uff_ssd/utils/inference_trt.py similarity index 70% rename from samples/python/uff_ssd/utils/inference.py rename to samples/python/uff_ssd/utils/inference_trt.py index a5186658..446b629b 100644 --- a/samples/python/uff_ssd/utils/inference.py +++ b/samples/python/uff_ssd/utils/inference_trt.py @@ -19,14 +19,12 @@ import sys import time import tensorrt as trt -import tensorflow as tf from PIL import Image import pycuda.driver as cuda -import pycuda.autoinit import numpy as np import utils.engine as engine_utils # TRT Engine creation/save/load utils -import utils.model as model_utils # UFF conversion uttils +from utils.modeldata import ModelData # ../../common.py sys.path.insert(1, @@ -98,7 +96,7 @@ class TRTInference(object): self.context = self.trt_engine.create_execution_context() # Allocate memory for multiple usage [e.g. multiple batch inference] - input_volume = trt.volume(model_utils.ModelData.INPUT_SHAPE) + input_volume = trt.volume(ModelData.INPUT_SHAPE) self.numpy_array = np.zeros((self.trt_engine.max_batch_size, input_volume)) def infer(self, image_path): @@ -163,7 +161,7 @@ class TRTInference(object): def _load_image_into_numpy_array(self, image): (im_width, im_height) = image.size return np.array(image).reshape( - (im_height, im_width, model_utils.ModelData.get_input_channels()) + (im_height, im_width, ModelData.get_input_channels()) ).astype(np.uint8) def _load_imgs(self, image_paths): @@ -176,8 +174,8 @@ class TRTInference(object): def _load_img(self, image_path): image = Image.open(image_path) - model_input_width = model_utils.ModelData.get_input_width() - model_input_height = model_utils.ModelData.get_input_height() + model_input_width = ModelData.get_input_width() + model_input_height = ModelData.get_input_height() # Note: Bilinear interpolation used by Pillow is a little bit # different than the one used by Tensorflow, so if network receives # an image that is not 300x300, the network output may differ @@ -194,65 +192,3 @@ class TRTInference(object): img_np = img_np.ravel() return img_np - -# This class is similar as TRTInference inference, but it manages Tensorflow -class TensorflowInference(object): - def __init__(self, pb_model_path): - self.detection_graph = tf.Graph() - with self.detection_graph.as_default(): - od_graph_def = tf.GraphDef() - with tf.gfile.GFile(pb_model_path, 'rb') as fid: - serialized_graph = fid.read() - od_graph_def.ParseFromString(serialized_graph) - tf.import_graph_def(od_graph_def, name='') - self.sess = tf.Session(graph=self.detection_graph) - - def infer(self, image_path): - img_np = self._load_img(image_path) - return self._run_tensorflow_graph(np.expand_dims(img_np, axis=0)) - - def infer_batch(self, image_paths): - img_np = self._load_imgs(image_paths) - return self._run_tensorflow_graph(img_np) - - def _run_tensorflow_graph(self, image_input): - ops = self.detection_graph.get_operations() - all_tensor_names = {output.name for op in ops for output in op.outputs} - tensor_dict = {} - for key in [ - 'num_detections', 'detection_boxes', - 'detection_scores', 'detection_classes' - ]: - tensor_name = key + ':0' - if tensor_name in all_tensor_names: - tensor_dict[key] = self.detection_graph.get_tensor_by_name( - tensor_name) - - image_tensor = self.detection_graph.get_tensor_by_name('image_tensor:0') - output_dict = self.sess.run(tensor_dict, - feed_dict={image_tensor: image_input}) - - # All outputs are float32 numpy arrays, so convert types as appropriate - output_dict['num_detections'] = output_dict['num_detections'].astype(np.int32) - output_dict['detection_classes'] = output_dict[ - 'detection_classes'].astype(np.uint8) - - return output_dict - - def _load_image_into_numpy_array(self, image): - (im_width, im_height) = image.size - return np.array(image).reshape( - (im_height, im_width, model_utils.ModelData.get_input_channels()) - ).astype(np.uint8) - - def _load_imgs(self, image_paths): - numpy_array = np.zeros((len(image_paths),) + (300, 300, 3)) - for idx, image_path in enumerate(image_paths): - img_np = self._load_img(image_path) - numpy_array[idx] = img_np - return numpy_array - - def _load_img(self, image_path): - img = Image.open(image_path) - img_np = self._load_image_into_numpy_array(img) - return img_np diff --git a/samples/python/uff_ssd/utils/model.py b/samples/python/uff_ssd/utils/model.py index a326bf49..5a407d32 100644 --- a/samples/python/uff_ssd/utils/model.py +++ b/samples/python/uff_ssd/utils/model.py @@ -14,7 +14,7 @@ # limitations under the License. # -# Model download and UFF convertion utils +# Model extraction and UFF convertion utils import os import sys import tarfile @@ -28,34 +28,10 @@ import time import math from utils.paths import PATHS - -sys.path.insert(1, os.path.join(sys.path[0], os.path.pardir)) -from common import retry +from utils.modeldata import ModelData # UFF conversion functionality -# This class contains converted (UFF) model metadata -class ModelData(object): - # Name of input node - INPUT_NAME = "Input" - # CHW format of model input - INPUT_SHAPE = (3, 300, 300) - # Name of output node - OUTPUT_NAME = "NMS" - - @staticmethod - def get_input_channels(): - return ModelData.INPUT_SHAPE[0] - - @staticmethod - def get_input_height(): - return ModelData.INPUT_SHAPE[1] - - @staticmethod - def get_input_width(): - return ModelData.INPUT_SHAPE[2] - - def ssd_unsupported_nodes_to_plugin_nodes(ssd_graph): """Makes ssd_graph TensorRT comparible using graphsurgeon. @@ -172,7 +148,7 @@ def model_to_uff(model_path, output_uff_path, silent=False): ) -# Model download functionality +# Model extraction functionality def maybe_print(should_print, print_arg): """Prints message if supplied boolean flag is true. @@ -194,75 +170,23 @@ def maybe_mkdir(dir_path): os.makedirs(dir_path) -def download_file(file_url, file_dest_path, silent=False): - """Downloads file from supplied URL and puts it into supplied directory. +def _extract_model(silent=False): + """Extract model from Tensorflow model zoo. Args: - file_url (str): URL with file to download - file_dest_path (str): path to save downloaded file in - silent (bool): if False, writes progress messages to stdout - """ - - @retry(n_retries=3) - def _download_file(file_url, file_dest, silent=False): - response = requests.get(file_url, stream=True) - total_length = response.headers.get('content-length') - - def print_progress(pct_done): - isatty = sys.stdout.isatty() - clear_char = "\r" if isatty else "" - endl_char = "" if isatty else "\n" - progress_bar_width = int(math.floor(pct_done * 50 / 100.0)) - sys.stdout.write("{}Download progress [{}{}] {:.2f}%{}".format( - clear_char, - "=" * progress_bar_width, - " " * (50 - progress_bar_width), - pct_done, - endl_char)) - sys.stdout.flush() - - if total_length is None or silent: # no content length header or silent, just write file - f.write(response.content) - else: # not silent, print progress - dl = 0 - total_length = int(total_length) - t_last_update = t_cur = time.time() - for data in response.iter_content(chunk_size=(4096 * 1024)): - dl += len(data) - file_dest.write(data) - if t_cur - t_last_update > 2.0: - print_progress(100 * dl / total_length) - t_last_update = t_cur - t_cur = time.time() - print_progress(100) - sys.stdout.write("\n") - - with open(file_dest_path, "wb") as f: - maybe_print(not silent, "Downloading {}".format(file_dest_path)) - _download_file(file_url, f, silent=silent) - -def download_model(model_name, silent=False): - """Downloads model_name from Tensorflow model zoo. - - Args: - model_name (str): chosen object detection model silent (bool): if False, writes progress messages to stdout """ maybe_print(not silent, "Preparing pretrained model") model_dir = PATHS.get_models_dir_path() maybe_mkdir(model_dir) - model_url = PATHS.get_model_url(model_name) - model_archive_path = os.path.join(model_dir, "{}.tar.gz".format(model_name)) - download_file(model_url, model_archive_path, silent=True) - maybe_print(not silent, "Download complete\nUnpacking {}".format(model_archive_path)) + model_archive_path = PATHS.get_data_file_path('ssd_inception_v2_coco_2017_11_17.tar.gz') + maybe_print(not silent, "Unpacking {}".format(model_archive_path)) with tarfile.open(model_archive_path, "r:gz") as tar: tar.extractall(path=model_dir) - maybe_print(not silent, "Extracting complete\nRemoving {}".format(model_archive_path)) - os.remove(model_archive_path) maybe_print(not silent, "Model ready") def prepare_ssd_model(model_name="ssd_inception_v2_coco_2017_11_17", silent=False): - """Downloads pretrained object detection model and converts it to UFF. + """Extract pretrained object detection model and converts it to UFF. The model is downloaded from Tensorflow object detection model zoo. Currently only ssd_inception_v2_coco_2017_11_17 model is supported @@ -275,7 +199,7 @@ def prepare_ssd_model(model_name="ssd_inception_v2_coco_2017_11_17", silent=Fals if model_name != "ssd_inception_v2_coco_2017_11_17": raise NotImplementedError( "Model {} is not supported yet".format(model_name)) - download_model(model_name, silent) + _extract_model(silent) ssd_pb_path = PATHS.get_model_pb_path(model_name) ssd_uff_path = PATHS.get_model_uff_path(model_name) model_to_uff(ssd_pb_path, ssd_uff_path, silent) diff --git a/samples/opensource/sampleMovieLensMPS/preprocess.py b/samples/python/uff_ssd/utils/modeldata.py similarity index 54% rename from samples/opensource/sampleMovieLensMPS/preprocess.py rename to samples/python/uff_ssd/utils/modeldata.py index 0758d605..3d619abc 100644 --- a/samples/opensource/sampleMovieLensMPS/preprocess.py +++ b/samples/python/uff_ssd/utils/modeldata.py @@ -14,10 +14,24 @@ # limitations under the License. # -import graphsurgeon as gs -import tensorflow as tf +# This class contains converted (UFF) model metadata +class ModelData(object): + # Name of input node + INPUT_NAME = "Input" + # CHW format of model input + INPUT_SHAPE = (3, 300, 300) + # Name of output node + OUTPUT_NAME = "NMS" + + @staticmethod + def get_input_channels(): + return ModelData.INPUT_SHAPE[0] + + @staticmethod + def get_input_height(): + return ModelData.INPUT_SHAPE[1] + + @staticmethod + def get_input_width(): + return ModelData.INPUT_SHAPE[2] -def preprocess(dynamic_graph): - axis = dynamic_graph.find_nodes_by_path("concatenate/concat/axis")[0] - # Set axis to 2, because of discrepancies between TensorFlow and TensorRT. - axis.attr["value"].tensor.int_val[0] = 2 diff --git a/samples/python/uff_ssd/utils/paths.py b/samples/python/uff_ssd/utils/paths.py index 67ad834d..c3ce6e1e 100644 --- a/samples/python/uff_ssd/utils/paths.py +++ b/samples/python/uff_ssd/utils/paths.py @@ -32,18 +32,19 @@ class Paths(object): ) self._VOC_DIR_PATH = \ os.path.join(self._SAMPLE_ROOT, 'VOCdevkit', 'VOC2007') + self._DATA_DIR_PATH = None # User configurable paths + def set_data_dir_path(self, data_dir): + self._DATA_DIR_PATH = data_dir + def set_workspace_dir_path(self, workspace_dir): self._WORKSPACE_DIR_PATH = workspace_dir def get_workspace_dir_path(self): return self._WORKSPACE_DIR_PATH - def set_voc_dir_path(self, voc_dir_path): - self._VOC_DIR_PATH = voc_dir_path - def get_voc_dir_path(self): return self._VOC_DIR_PATH @@ -70,6 +71,9 @@ class Paths(object): inference_type_to_str[inference_type], 'engine_bs_{}.buf'.format(max_batch_size)) + def get_data_file_path(self, path): + return os.path.join(self._DATA_DIR_PATH, path) + def get_voc_annotation_cache_path(self): return os.path.join(self.get_workspace_dir_path(), 'annotations_cache') @@ -102,9 +106,6 @@ class Paths(object): else: return self.get_voc_tensorrt_model_detections_path(use_fp16) - def get_model_url(self, model_name): - return 'http://download.tensorflow.org/models/object_detection/{}.tar.gz'.format(model_name) - def get_model_dir_path(self, model_name): return os.path.join(self.get_models_dir_path(), model_name) @@ -129,6 +130,8 @@ class Paths(object): error = self._verify_voc_paths() if not os.path.exists(self.get_workspace_dir_path()): error = True + if not os.path.exists(self._DATA_DIR_PATH): + error = True if error: print("An error occured when running the script.") @@ -167,7 +170,7 @@ class Paths(object): print( "Error: {}\n{}\n{}".format( "Incomplete VOC dataset detected (voc_dir: {})".format(voc_dir), - "Try redownloading VOC or check if --voc_dir is set up correctly", + "Try redownloading VOC or check if --data is set up correctly", "For more details, check README.md" ) ) diff --git a/samples/python/uff_ssd/voc_evaluation.py b/samples/python/uff_ssd/voc_evaluation.py index c7109017..8f680165 100644 --- a/samples/python/uff_ssd/voc_evaluation.py +++ b/samples/python/uff_ssd/voc_evaluation.py @@ -15,30 +15,20 @@ # limitations under the License. # -import sys -import os -import ctypes -import time import argparse import glob +import os +import tarfile -if sys.version_info[0] == 2: - import xml.etree.cElementTree as ET -else: - import xml.etree.ElementTree as ET - -import numpy as np +import pycuda.autoinit import tensorrt as trt -from PIL import Image - +import utils.coco as coco_utils # COCO dataset descriptors # Utility functions -import utils.inference as inference_utils # TRT/TF inference wrappers -import utils.model as model_utils # UFF conversion -import utils.mAP as voc_mAP_utils # mAP computation -import utils.voc as voc_utils # VOC dataset descriptors -import utils.coco as coco_utils # COCO dataset descriptors -from utils.paths import PATHS # Path management - +import utils.mAP as voc_mAP_utils # mAP computation +from utils.modeldata import ModelData +import utils.voc as voc_utils # VOC dataset descriptors +from PIL import Image +from utils.paths import PATHS # Path management # VOC and COCO label lists VOC_CLASSES = voc_utils.VOC_CLASSES_LIST @@ -135,10 +125,10 @@ def analyze_tensorrt_prediction(detection_out, pred_start_idx): xmax = fetch_prediction_field("xmax", detection_out, pred_start_idx) ymax = fetch_prediction_field("ymax", detection_out, pred_start_idx) - xmin = float(xmin) * model_utils.ModelData.get_input_width() - ymin = float(ymin) * model_utils.ModelData.get_input_height() - xmax = float(xmax) * model_utils.ModelData.get_input_width() - ymax = float(ymax) * model_utils.ModelData.get_input_height() + xmin = float(xmin) * ModelData.get_input_width() + ymin = float(ymin) * ModelData.get_input_height() + xmax = float(xmax) * ModelData.get_input_width() + ymax = float(ymax) * ModelData.get_input_height() return image_id, label, confidence, xmin, ymin, xmax, ymax @@ -169,7 +159,7 @@ def produce_tensorrt_detections(detection_files, trt_inference_wrapper, max_batc Args: detection_files (dict): dictionary that maps class labels to class result files - trt_inference_wrapper (inference_utils.TRTInference): + trt_inference_wrapper (TRTInference): internal Python class wrapping TensorRT inferece setup/run code batch_size (int): batch size used for inference @@ -224,7 +214,7 @@ def produce_tensorflow_detections(detection_files, tf_inference_wrapper, batch_s Args: detection_files (dict): dictionary that maps class labels to class result files - tf_inference_wrapper (inference_utils.TensorflowInference): + tf_inference_wrapper (TensorflowInference): internal Python class wrapping Tensorflow inferece setup/run code batch_size (int): batch size used for inference @@ -250,10 +240,10 @@ def produce_tensorflow_detections(detection_files, tf_inference_wrapper, batch_s # Output bounding boxes are in [0, 1] format, # here we rescale them to pixel [0, 255] format ymin, xmin, ymax, xmax = bbox - xmin = float(xmin) * model_utils.ModelData.get_input_width() - ymin = float(ymin) * model_utils.ModelData.get_input_height() - xmax = float(xmax) * model_utils.ModelData.get_input_width() - ymax = float(ymax) * model_utils.ModelData.get_input_height() + xmin = float(xmin) * ModelData.get_input_width() + ymin = float(ymin) * ModelData.get_input_height() + xmax = float(xmax) * ModelData.get_input_width() + ymax = float(ymax) * ModelData.get_input_height() # Detection is saved only if confidence is bigger than zero if confidence > 0.0: @@ -332,30 +322,41 @@ def preprocess_voc(): img_pil = Image.open(voc_jpeg_path) img_pil = img_pil.resize( size=( - model_utils.ModelData.get_input_width(), - model_utils.ModelData.get_input_height()), + ModelData.get_input_width(), + ModelData.get_input_height()), resample=Image.BILINEAR ) img_pil.save(voc_ppm_path) -def adjust_paths(args): +def adjust_paths(args, data_dir): """Adjust all file/directory paths, arguments passed by user. During script launch, user can pass several arguments to the script - (e.g. --workspace_dir, --voc_dir), that define where script will look + (e.g. --workspace_dir, --data), that define where script will look for the files needed for execution. This function adjusts internal Paths Python datastructure to accomodate for changes from defaults requested by user through appropriate command line arguments. Args: args (argparse.Namespace): parsed user arguments + data_dir (str): path to the data directory """ - if args.voc_dir: - PATHS.set_voc_dir_path(args.voc_dir) if args.workspace_dir: PATHS.set_workspace_dir_path(args.workspace_dir) if not os.path.exists(PATHS.get_workspace_dir_path()): os.makedirs(PATHS.get_workspace_dir_path()) + PATHS.set_data_dir_path(data_dir) + + +def extract_voc_data_if_needed(): + if os.path.exists(PATHS.get_voc_dir_path()): + return + voc_archive_path = PATHS.get_data_file_path('VOCtest_06-Nov-2007.tar') + print("Unpacking {}".format(voc_archive_path)) + with tarfile.open(voc_archive_path, "r") as tar: + tar.extractall(path=PATHS.get_sample_root()) + print("Unpacking done!") + def parse_commandline_arguments(): """Parses command line arguments and adjusts internal data structures.""" @@ -373,15 +374,18 @@ def parse_commandline_arguments(): help='force model inference even if detections exist') parser.add_argument('-w', '--workspace_dir', help='sample workspace directory') - parser.add_argument('-voc', '--voc_dir', - help='VOC2007 root directory') + parser.add_argument('-d', '--data', + help="Specify the data directory where it is saved in. $TRT_DATA_DIR will be overwritten by this argument.") - # Parse arguments passed - args = parser.parse_args() + args, _ = parser.parse_known_args() - # Adjust global Paths data structure - adjust_paths(args) + data_dir = os.environ.get('TRT_DATA_DIR', None) if args.data is None else args.data + if data_dir is None: + raise ValueError("Data directory must be specified by either `-d $DATA` or environment variable $TRT_DATA_DIR.") + adjust_paths(args, data_dir) + + extract_voc_data_if_needed() # Verify Paths after adjustments. This also exits script if verification fails PATHS.verify_all_paths(should_verify_voc=True) @@ -413,8 +417,7 @@ def parse_commandline_arguments(): } return parsed - -if __name__ == '__main__': +def main(): # Parse command line arguments parsed = parse_commandline_arguments() @@ -438,8 +441,6 @@ if __name__ == '__main__': # ...and .uff path, if needed (converting .pb to .uff if not already done) if parsed['inference_backend'] == 'tensorrt': ssd_model_uff_path = PATHS.get_model_uff_path(MODEL_NAME) - if not os.path.exists(ssd_model_uff_path): - model_utils.prepare_ssd_model(MODEL_NAME) # This block of code sets up and performs inference, if needed if not skip_inference: @@ -458,7 +459,8 @@ if __name__ == '__main__': # TRTInference initialization initializes # all TensorRT structures, creates engine if it doesn't # already exist and finally saves it to file for future uses - trt_inference_wrapper = inference_utils.TRTInference( + from utils.inference_trt import TRTInference + trt_inference_wrapper = TRTInference( parsed['trt_engine_path'], ssd_model_uff_path, parsed['trt_engine_datatype'], parsed['max_batch_size']) # Outputs from TensorRT are handled differently than @@ -470,8 +472,8 @@ if __name__ == '__main__': elif parsed['inference_backend'] == 'tensorflow': # In case of Tensorflow all we need to # initialize inference is frozen model... - tf_inference_wrapper = \ - inference_utils.TensorflowInference(ssd_model_pb_path) + from utils.inference_tf import TensorflowInference + tf_inference_wrapper = TensorflowInference(ssd_model_pb_path) # ...and after initializing it, we can # proceed to producing detections produce_tensorflow_detections(detection_files, @@ -489,3 +491,7 @@ if __name__ == '__main__': # Close detection files, they are not needed anymore for key in detection_files: detection_files[key].close() + + +if __name__ == '__main__': + main() diff --git a/samples/python/yolov3_onnx/README.md b/samples/python/yolov3_onnx/README.md index d641461d..2231a791 100644 --- a/samples/python/yolov3_onnx/README.md +++ b/samples/python/yolov3_onnx/README.md @@ -29,61 +29,56 @@ After inference, post-processing including bounding-box clustering is applied. T For specific software versions, see the [TensorRT Installation Guide](https://docs.nvidia.com/deeplearning/sdk/tensorrt-archived/index.html). 1. Install the dependencies for Python. - ```bash - python3 -m pip install -r requirements.txt - ``` + ```sh + python3 -m pip install -r requirements.txt + ``` + +2. Download sample data. See the "Download Sample Data" section of [the general setup guide](../README.md). + ## Running the sample -1. Create an ONNX version of YOLOv3 with the following command. The Python script will also download all necessary files from the official mirrors (only once). +The data directory needs to be specified (either via `-d /path/to/data` or environment varaiable `TRT_DATA_DIR`) +when running these scripts. An error will be thrown if not. - ```bash - python3 yolov3_to_onnx.py - ``` +1. Create an ONNX version of YOLOv3 with the following command. + ```sh + python3 yolov3_to_onnx.py + ``` + When running the above command for the first time, the output should look similar to the following: + ``` + [...] + %106_convolutional = Conv[auto_pad = u'SAME_LOWER', dilations = [1, 1], kernel_shape = [1, 1], strides = [1, 1]] + (%105_convolutional_lrelu, %106_convolutional_conv_weights, %106_convolutional_conv_bias) + return %082_convolutional, %094_convolutional,%106_convolutional + } + ``` - When running the above command for the first time, the output should look similar to the following: - ``` - Downloading from https://raw.githubusercontent.com/pjreddie/darknet/f86901f6177dfc6116360a13cc06ab680e0c86b0/cfg/yolov3.cfg, this may take a while... - 100% [................................................................................] 8342 / 8342 - Downloading from master.dl.sourceforge.net/project/darknet-yolo.mirror/darknet_yolo_v3_optimal/yolov3.weights, this may take a while... - 100% [................................................................................] 248007048 / 248007048 - [...] - %106_convolutional = Conv[auto_pad = u'SAME_LOWER', dilations = [1, 1], kernel_shape = [1, 1], strides = [1, 1]] - (%105_convolutional_lrelu, %106_convolutional_conv_weights, %106_convolutional_conv_bias) - return %082_convolutional, %094_convolutional,%106_convolutional - } - ``` - -2. Build a TensorRT engine from the generated ONNX file and run inference on a sample image, which will also be downloaded during the first run. - ```bash - python3 onnx_to_tensorrt.py - ``` - - When running the above command for the first time, the output should look similar to the following: - ``` - Downloading from https://github.com/pjreddie/darknet/raw/f86901f6177dfc6116360a13cc06ab680e0c86b0/data/dog.jpg, this may take a while... - 100% [................................................................................] 163759 / 163759 - Building an engine from file yolov3.onnx, this may take a while... - Running inference on image dog.jpg... - Saved image with bounding boxes of detected objects to dog_bboxes.jpg. - ``` +2. Build a TensorRT engine from the generated ONNX file and run inference on a sample image + ```sh + python3 onnx_to_tensorrt.py + ``` + When running the above command for the first time, the output should look similar to the following: + ``` + Building an engine from file yolov3.onnx, this may take a while... + Running inference on image dog.jpg... + Saved image with bounding boxes of detected objects to dog_bboxes.jpg. + ``` 3. Verify that the sample ran successfully. If the sample runs successfully you should see output similar to the following: - ``` - Downloading from https://github.com/pjreddie/darknet/raw/f86901f6177dfc6116360a13cc06ab680e0c86b0/data/dog.jpg, this may take a while… - 100% [......................................................................] 163759 / 163759 - Loading ONNX file from path yolov3.onnx... - Beginning ONNX file parsing - Completed parsing of ONNX file - Building an engine from file yolov3.onnx; this may take a while... - Completed creating Engine - Running inference on image dog.jpg... - [[135.14841333 219.59879284 184.30209195 324.0265199 ] - [ 98.30805074 135.72613533 499.71263299 299.25579652] - [478.00605802 81.25702449 210.57787895 86.91502688]] [0.99854713 0.99880403 0.93829258] [16 1 7] - Saved image with bounding boxes of detected objects to dog_bboxes.png. - ``` - You should be able to visually confirm whether the detection was correct. + ``` + Loading ONNX file from path yolov3.onnx... + Beginning ONNX file parsing + Completed parsing of ONNX file + Building an engine from file yolov3.onnx; this may take a while... + Completed creating Engine + Running inference on image dog.jpg... + [[135.14841333 219.59879284 184.30209195 324.0265199 ] + [ 98.30805074 135.72613533 499.71263299 299.25579652] + [478.00605802 81.25702449 210.57787895 86.91502688]] [0.99854713 0.99880403 0.93829258] [16 1 7] + Saved image with bounding boxes of detected objects to dog_bboxes.png. + ``` + You should be able to visually confirm whether the detection was correct. # Additional resources diff --git a/samples/python/yolov3_onnx/onnx_to_tensorrt.py b/samples/python/yolov3_onnx/onnx_to_tensorrt.py index 93a8fab8..aac75467 100644 --- a/samples/python/yolov3_onnx/onnx_to_tensorrt.py +++ b/samples/python/yolov3_onnx/onnx_to_tensorrt.py @@ -14,7 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - from __future__ import print_function import numpy as np @@ -28,6 +27,7 @@ from data_processing import PreprocessYOLO, PostprocessYOLO, ALL_CATEGORIES import sys, os sys.path.insert(1, os.path.join(sys.path[0], "..")) import common +from downloader import getFilePath TRT_LOGGER = trt.Logger() @@ -63,7 +63,7 @@ def get_engine(onnx_file_path, engine_file_path=""): """Attempts to load a serialized engine if available, otherwise builds a new TensorRT engine and saves it.""" def build_engine(): """Takes an ONNX file and creates a TensorRT engine to run inference with""" - with trt.Builder(TRT_LOGGER) as builder, builder.create_network(common.EXPLICIT_BATCH) as network, builder.create_builder_config() as config, trt.OnnxParser(network, TRT_LOGGER) as parser: + with trt.Builder(TRT_LOGGER) as builder, builder.create_network(common.EXPLICIT_BATCH) as network, builder.create_builder_config() as config, trt.OnnxParser(network, TRT_LOGGER) as parser, trt.Runtime(TRT_LOGGER) as runtime: config.max_workspace_size = 1 << 28 # 256MiB builder.max_batch_size = 1 # Parse model file @@ -82,10 +82,11 @@ def get_engine(onnx_file_path, engine_file_path=""): network.get_input(0).shape = [1, 3, 608, 608] print('Completed parsing of ONNX file') print('Building an engine from file {}; this may take a while...'.format(onnx_file_path)) - engine = builder.build_engine(network, config) + plan = builder.build_serialized_network(network, config) + engine = runtime.deserialize_cuda_engine(plan) print("Completed creating Engine") with open(engine_file_path, "wb") as f: - f.write(engine.serialize()) + f.write(plan) return engine if os.path.exists(engine_file_path): @@ -103,9 +104,7 @@ def main(): onnx_file_path = 'yolov3.onnx' engine_file_path = "yolov3.trt" # Download a dog image and save it to the following file path: - input_image_path = common.download_file('dog.jpg', - 'https://github.com/pjreddie/darknet/raw/f86901f6177dfc6116360a13cc06ab680e0c86b0/data/dog.jpg', checksum_reference=None) - + input_image_path = getFilePath('samples/python/yolov3_onnx/dog.jpg') # Two-dimensional tuple with the target network's (spatial) input resolution in HW ordered input_resolution_yolov3_HW = (608, 608) # Create a pre-processor object by specifying the required input resolution for YOLOv3 diff --git a/samples/python/yolov3_onnx/requirements.txt b/samples/python/yolov3_onnx/requirements.txt index 499c7fe9..fd3adff6 100644 --- a/samples/python/yolov3_onnx/requirements.txt +++ b/samples/python/yolov3_onnx/requirements.txt @@ -1,6 +1,5 @@ +numpy>=1.15.1 protobuf>=3.11.3 -onnx==1.7.0 +onnx==1.8.0 +pycuda<2021.1 Pillow>=8.1.2 -pycuda -numpy -wget diff --git a/samples/python/yolov3_onnx/yolov3_to_onnx.py b/samples/python/yolov3_onnx/yolov3_to_onnx.py index 5555d5cd..f70d7815 100644 --- a/samples/python/yolov3_onnx/yolov3_to_onnx.py +++ b/samples/python/yolov3_onnx/yolov3_to_onnx.py @@ -17,19 +17,17 @@ from __future__ import print_function from collections import OrderedDict -import os.path +import sys +import os import onnx from onnx import helper from onnx import TensorProto import numpy as np -import sys, os sys.path.insert(1, os.path.join(sys.path[0], os.path.pardir)) -import common +from downloader import getFilePath -sys.path.insert(1, os.path.join(sys.path[0], os.path.pardir)) -from common import retry_call class DarkNetParser(object): """Definition of a parser for DarkNet-based YOLOv3-608 (only tested for this topology).""" @@ -708,12 +706,7 @@ class GraphBuilderONNX(object): def main(): """Run the DarkNet-to-ONNX conversion for YOLOv3-608.""" - # Download the config for YOLOv3 if not present yet, and analyze the checksum: - cfg_file_path = common.download_file( - 'yolov3.cfg', - 'https://raw.githubusercontent.com/pjreddie/darknet/f86901f6177dfc6116360a13cc06ab680e0c86b0/cfg/yolov3.cfg', - 'b969a43a848bbf26901643b833cfb96c') - + cfg_file_path = getFilePath('samples/python/yolov3_onnx/yolov3.cfg') # These are the only layers DarkNetParser will extract parameters from. The three layers of # type 'yolo' are not parsed in detail because they are included in the post-processing later: supported_layers = ['net', 'convolutional', 'shortcut', @@ -736,12 +729,7 @@ def main(): # Create a GraphBuilderONNX object with the known output tensor dimensions: builder = GraphBuilderONNX(output_tensor_dims) - # We want to populate our network with weights later, that's why we download those from - # the official mirror (and verify the checksum): - weights_file_path = common.download_file( - 'yolov3.weights', - 'https://master.dl.sourceforge.net/project/darknet-yolo.mirror/darknet_yolo_v3_optimal/yolov3.weights', - 'c84e5b99d0e52cd466ae710cadf6d84c') + weights_file_path = getFilePath('samples/python/yolov3_onnx/yolov3.weights') # Now generate an ONNX graph with weights from the previously parsed layer configurations # and the weights file: diff --git a/tools/Polygraphy/CHANGELOG.md b/tools/Polygraphy/CHANGELOG.md index b5c7e79a..222c4330 100644 --- a/tools/Polygraphy/CHANGELOG.md +++ b/tools/Polygraphy/CHANGELOG.md @@ -3,6 +3,75 @@ Dates are in YYYY-MM-DD format. +## v0.30.3 (2021-06-25) +### Fixed +- Fixed various typos, added more details to some tool READMEs. + + +## v0.30.2 (2021-06-15) +### Changed +- Added `polygraphy.config` as a top-level import so that it no longer needs to be imported separately + (i.e. `from polygraphy import config`). + +### Fixed +- Fixed a bug where `surgeon sanitize` would not re-run shape inference after overriding model input + shapes, causing constant folding to be sub-optimal. + + +## v0.30.1 (2021-06-07) +### Changed +- CLI tools will no longer print long stack traces on user error. + +### Fixed +- Fixed a bug where `surgeon` subtools would not work with ONNX models without an `.onnx` extension. +- Fixed a bug where `surgeon insert` would not correctly run shape inference if the inserted node replaced + the graph outputs. +- Fixed a bug where `POLYGRAPHY_AUTOINSTALL_DEPS` would not work correctly for nested modules, + e.g. `mod.lazy_import("onnx.shape_inference")`. + + +## v0.30.0 (2021-05-26) +### Added +- Added an `--ignore-fail-code` option to `debug` subtools to ignore certain types of failures. +- Added a highly experimental `OnnxLikeFromNetwork` loader that can generate a file using the ONNX + format based on a TensorRT network. The resulting model is **not** valid ONNX, but is useful for visualization. +- Added a `onnx-like-trt-network` type in `convert` to generate ONNX-like models from TensorRT networks. +- Added support for custom installation commands during dependency autoinstall. + This can be configured using `config.INSTALL_CMD` or by setting the `POLYGRAPHY_INSTALL_CMD` environment variable. +- Added support for loading external data in `InferShapes`. +- Added a `--no-per-pass-shape-inference` argument to `surgeon sanitize` to disable shape inference + between constant-folding passes. +- Added a `--external-data-size-threshold` CLI option for saving external data for ONNX models. +- Added a `--no-save-all-tensors-to-one-file` CLI option to avoid saving ONNX external data to a single file. + +### Changed +- Improved logic for auto-permuting tensors in `basic_compare_func`. The new logic can handle + an arbitrary number of dimensions. For example, if two tensors with shapes `(1, 3, 45, 45, 45)` + and `(1, 45, 45, 45, 3)` are being compared, `basic_compare_func` will now guess that the latter + should be transposed using a permutation of `(0, 4, 1, 2, 3)` to match the former. +- Improved display of `Profile` in logging messages. +- Updated NumPy array encoding to use `base64`. In some cases, this can reduce file sizes by a factor of 4. +- Updated `debug precision` default direction to `forward` as this typically leads to better results. +- Added a `--no-strict-types` flag to `debug precision` in case strict types needs to be disabled for any reason. +- `FoldConstants` will no longer run shape inference if shape folding is disabled. +- `InferShapes` will now automatically write large models to the disk to work around the 2 GiB protobuf size limitation. + The threshold can be configured using the `save_to_disk_threshold_bytes` parameter. + +### Fixed +- Fixed a bug in `inspect model` where engine output bindings would all be printed on one line. +- Fixed a bug where using `set_profile` in the `TrtRunner` would sometimes cause input shape + checks in `infer` to fail even when shapes were valid. +- Fixed a bug in `inspect model` where engine output bindings would display the wrong shapes + for profiles after the first. +- Fixed a bug where `debug precision` would incorrectly mark constant layer outputs and non-execution tensors + to run in higher precision. +- Fixed a bug where `debug precision` would crash if engine building failed. It now continues to the next iteration, + counting the previous one as a failure. +- Fixed a bug where `InferShapes` would require `--external-data-dir` to be set even if the external data + were in the same directory as the model. +- Fixed a bug where `--data-loader-script` would not provide data in the `run` tool if int8 calibration were enabled in TensorRT. + + ## v0.29.2 (2021-04-30) ### Added - Added a `--log-file` option to CLI tools to store logging output to a file. diff --git a/tools/Polygraphy/CONTRIBUTING.md b/tools/Polygraphy/CONTRIBUTING.md index bae990ab..cbf06089 100644 --- a/tools/Polygraphy/CONTRIBUTING.md +++ b/tools/Polygraphy/CONTRIBUTING.md @@ -90,7 +90,7 @@ specified in `remove_in`. 2. Make your changes 3. Run Tests: - Install prerequisite packages with: - - `python3 -m pip install -r tests/requirements.txt` - - `python3 -m pip install -r docs/requirements.txt` + - `python3 -m pip install -r tests/requirements.txt --index-url https://pypi.ngc.nvidia.com` + - `python3 -m pip install -r docs/requirements.txt --index-url https://pypi.ngc.nvidia.com` - Run tests with: `make test` 4. Commit, push, and submit a merge request to the main branch diff --git a/tools/Polygraphy/README.md b/tools/Polygraphy/README.md index 4542c960..723d6ee5 100644 --- a/tools/Polygraphy/README.md +++ b/tools/Polygraphy/README.md @@ -79,26 +79,38 @@ python3 -m pip install colored ### Installing Dependencies -Each `backend` directory includes a `requirements.txt` file that specifies the minimum set of packages -it depends on. You can install the requirements for whichever backends you're interested in -using: +Polygraphy has no hard-dependencies on other Python packages. However, much of the functionality included +does require other Python packages. +#### Automatically Installing Dependencies + +It's non-trivial to determine all the packages that will be required ahead of time, +since it depends on exactly what functionality is being used. + +To make this easier, Polygraphy can optionally automatically install or upgrade dependencies at runtime, as they are needed. +To enable this behavior, set the `POLYGRAPHY_AUTOINSTALL_DEPS` environment variable to `1` or +`polygraphy.config.AUTOINSTALL_DEPS = True` using the Python API. + +NOTE: By default, dependencies will be installed using the current interpreter, and may overwrite existing +packages. The default installation command, which is `python3 -m pip install`, can be overriden by setting +the `POLYGRAPHY_INSTALL_CMD` environment variable, or setting `polygraphy.config.INSTALL_CMD = "..."` using the Python API. + +#### Installing Manually + +Each `backend` directory includes a `requirements.txt` file that specifies the minimum set of packages +it depends on. This does not necessarily include all packages required for all the functionality provided +by the backend, but does serve as a good starting point. + +You can install the requirements for whichever backends you're interested in with: ```bash python3 -m pip install -r polygraphy/backend//requirements.txt ``` -#### Automatically Installing Dependencies - -Polygraphy has no hard-dependencies on other Python packages. However, much of the functionality included -does require other Python packages. It's non-trivial to determine what packages will be required -ahead of time, since it depends on exactly what functionality is being used. - -To make this easier, Polygraphy can optionally automatically install or upgrade dependencies as they are needed. -To enable this behavior, set the `POLYGRAPHY_AUTOINSTALL_DEPS` environment variable to `1`. - -NOTE: The dependencies will be installed using the current interpreter, and may overwrite existing -packages. Thus, it may be desirable to use this feature in a Python virtual environment or container. - +If additional packages are required, warnings or errors will be logged. +You can install the additional packages manually with: +```bash +python3 -m pip install +``` ## Usage @@ -143,7 +155,8 @@ To view the docs, open `build/docs/index.html` in a browser or HTML viewer. Polygraphy includes various runtime checks for internal correctness, which are disabled by default. These checks can be enabled by setting the `POLYGRAPHY_INTERNAL_CORRECTNESS_CHECKS` -environment variable to `1`. A failure in this type of check indicates a bug in Polygraphy. +environment variable to `1` or `polygraphy.config.INTERNAL_CORRECTNESS_CHECKS = True` in the Python API. +A failure in this type of check indicates a bug in Polygraphy. When the checks are enabled, Polygraphy will ensure, for example, that loaders do not modify their state when they are called, and that runners will reset their state correctly in diff --git a/tools/Polygraphy/bin/polygraphy b/tools/Polygraphy/bin/polygraphy index d30b89fa..68dd3de8 100755 --- a/tools/Polygraphy/bin/polygraphy +++ b/tools/Polygraphy/bin/polygraphy @@ -33,11 +33,14 @@ sys.path.insert(0, G_ROOT_DIR) import polygraphy from polygraphy.logger import G_LOGGER from polygraphy.tools import TOOL_REGISTRY +from polygraphy.exception import PolygraphyException def main(): parser = argparse.ArgumentParser(description="Polygraphy: A Deep Learning Debugging Toolkit") - parser.add_argument("-v", "--version", action="version", version=G_LOGGER._str_from_module_info(polygraphy, name="Polygraphy")) + parser.add_argument( + "-v", "--version", action="version", version=G_LOGGER._str_from_module_info(polygraphy, name="Polygraphy") + ) subparsers = parser.add_subparsers(title="Tools", dest="tools") subparsers.required = True @@ -48,11 +51,19 @@ def main(): args, unknown = parser.parse_known_args() if unknown: - G_LOGGER.exit("Unrecognized Options: {:}".format(unknown)) + G_LOGGER.error("Unrecognized Options: {:}".format(unknown)) + return 1 G_LOGGER.verbose("Running Command: {:}".format(" ".join(sys.argv))) - sys.exit(args.subcommand(args)) + + try: + status = args.subcommand(args) + except PolygraphyException: + # `PolygraphyException`s indicate user error, so we need not display the stack trace. + status = 1 + + return status if __name__ == "__main__": - main() + sys.exit(main()) diff --git a/tools/Polygraphy/docs/conf.py b/tools/Polygraphy/docs/conf.py index 514ea298..568010f0 100644 --- a/tools/Polygraphy/docs/conf.py +++ b/tools/Polygraphy/docs/conf.py @@ -15,17 +15,18 @@ # import sys import os + ROOT_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), os.path.pardir) sys.path.insert(0, ROOT_DIR) import polygraphy extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.autosummary', - 'sphinx.ext.napoleon', - 'sphinx.ext.mathjax', - 'sphinx.ext.viewcode', + "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", + "sphinx.ext.autosummary", + "sphinx.ext.napoleon", + "sphinx.ext.mathjax", + "sphinx.ext.viewcode", ] # Want to be able to generate docs with no dependencies installed @@ -47,49 +48,47 @@ add_module_names = False autosummary_generate = True -source_suffix = ['.rst'] +source_suffix = [".rst"] # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'Polygraphy' -copyright = '2020, NVIDIA' -author = 'NVIDIA' +project = "Polygraphy" +copyright = "2020, NVIDIA" +author = "NVIDIA" version = polygraphy.__version__ # The full version, including alpha/beta/rc tags. release = version # Style -pygments_style = 'colorful' +pygments_style = "colorful" -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Use the TRT theme and NVIDIA logo -html_static_path = ['_static'] +html_static_path = ["_static"] -html_logo = '_static/img/nvlogo_white.png' +html_logo = "_static/img/nvlogo_white.png" # Hide source link html_show_sourcelink = False # Output file base name for HTML help builder. -htmlhelp_basename = 'TensorRTdoc' +htmlhelp_basename = "TensorRTdoc" # For constructor arguments to show up in Sphinx generated doc -autoclass_content = 'both' +autoclass_content = "both" # Unlimited depth sidebar. -html_theme_options = { - 'navigation_depth': -1 -} +html_theme_options = {"navigation_depth": -1} -html_sidebars = { '**': ['globaltoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html'] } +html_sidebars = {"**": ["globaltoc.html", "relations.html", "sourcelink.html", "searchbox.html"]} # Allows us to override the default page width in the Sphinx theme. def setup(app): - app.add_css_file('style.css') + app.add_css_file("style.css") LATEX_BUILDER = "sphinx.builders.latex" if LATEX_BUILDER in app.config.extensions: app.config.extensions.remove(LATEX_BUILDER) diff --git a/tools/Polygraphy/examples/api/00_inference_with_tensorrt/example.py b/tools/Polygraphy/examples/api/00_inference_with_tensorrt/example.py index f893826e..48783a9e 100644 --- a/tools/Polygraphy/examples/api/00_inference_with_tensorrt/example.py +++ b/tools/Polygraphy/examples/api/00_inference_with_tensorrt/example.py @@ -19,15 +19,15 @@ This script runs an identity model with TensorRT with FP16 precision enabled. """ import numpy as np -from polygraphy.backend.trt import (CreateConfig, EngineFromNetwork, - NetworkFromOnnxPath, SaveEngine, TrtRunner) +from polygraphy.backend.trt import CreateConfig, EngineFromNetwork, NetworkFromOnnxPath, SaveEngine, TrtRunner def main(): # We can compose multiple lazy loaders together to get the desired conversion. # In this case, we want ONNX -> TensorRT Network -> TensorRT engine (w/ fp16). - build_engine = EngineFromNetwork(NetworkFromOnnxPath("identity.onnx"), - config=CreateConfig(fp16=True)) # Note that config is an optional argument. + build_engine = EngineFromNetwork( + NetworkFromOnnxPath("identity.onnx"), config=CreateConfig(fp16=True) + ) # Note that config is an optional argument. # To reuse the engine elsewhere, we can serialize and save it to a file. # The `SaveEngine` lazy loader will return the TensorRT engine, which allows us to chain @@ -46,7 +46,9 @@ def main(): # Thus, if you want to store results from multiple inferences, you should use `copy.deepcopy()`. outputs = runner.infer(feed_dict={"x": inp_data}) - assert np.array_equal(outputs["y"], inp_data) # It's an identity model! + assert np.array_equal(outputs["y"], inp_data) # It's an identity model! + + print("Inference succeeded!") if __name__ == "__main__": diff --git a/tools/Polygraphy/examples/api/01_comparing_frameworks/example.py b/tools/Polygraphy/examples/api/01_comparing_frameworks/example.py index 4b769754..c4451e93 100644 --- a/tools/Polygraphy/examples/api/01_comparing_frameworks/example.py +++ b/tools/Polygraphy/examples/api/01_comparing_frameworks/example.py @@ -20,8 +20,7 @@ This script runs an identity model with ONNX-Runtime and TensorRT, then compares outputs. """ from polygraphy.backend.onnxrt import OnnxrtRunner, SessionFromOnnx -from polygraphy.backend.trt import (EngineFromNetwork, NetworkFromOnnxPath, - TrtRunner) +from polygraphy.backend.trt import EngineFromNetwork, NetworkFromOnnxPath, TrtRunner from polygraphy.comparator import Comparator diff --git a/tools/Polygraphy/examples/api/02_using_real_data/example.py b/tools/Polygraphy/examples/api/02_using_real_data/example.py index 53716e68..cb640cf3 100644 --- a/tools/Polygraphy/examples/api/02_using_real_data/example.py +++ b/tools/Polygraphy/examples/api/02_using_real_data/example.py @@ -20,15 +20,14 @@ This script uses the Polygraphy Runner API to validate the outputs of an identity model using a trivial dataset. """ import numpy as np -from polygraphy.backend.trt import (EngineFromNetwork, NetworkFromOnnxPath, - TrtRunner) +from polygraphy.backend.trt import EngineFromNetwork, NetworkFromOnnxPath, TrtRunner REAL_DATASET = [ np.ones((1, 1, 2, 2), dtype=np.float32), np.zeros((1, 1, 2, 2), dtype=np.float32), np.ones((1, 1, 2, 2), dtype=np.float32), np.zeros((1, 1, 2, 2), dtype=np.float32), -] # Definitely real data +] # Definitely real data # For our identity network, the golden output values are the same as the input values. # Though this network appears to do nothing, it can be incredibly useful in some cases (like here!). @@ -46,6 +45,8 @@ def main(): assert np.array_equal(outputs["y"], golden) + print("Validation succeeded!") + if __name__ == "__main__": main() diff --git a/tools/Polygraphy/examples/api/03_interoperating_with_tensorrt/example.py b/tools/Polygraphy/examples/api/03_interoperating_with_tensorrt/example.py index 24c6c931..3e5cd937 100644 --- a/tools/Polygraphy/examples/api/03_interoperating_with_tensorrt/example.py +++ b/tools/Polygraphy/examples/api/03_interoperating_with_tensorrt/example.py @@ -23,8 +23,7 @@ to print the network name and enable FP16 mode. import numpy as np import tensorrt as trt from polygraphy import func -from polygraphy.backend.trt import (CreateConfig, EngineFromNetwork, - NetworkFromOnnxPath, TrtRunner) +from polygraphy.backend.trt import CreateConfig, EngineFromNetwork, NetworkFromOnnxPath, TrtRunner # TIP: The immediately evaluated functional API makes it very easy to interoperate @@ -65,7 +64,9 @@ def main(): # Thus, if you want to store results from multiple inferences, you should use `copy.deepcopy()`. outputs = runner.infer({"x": inp_data}) - assert np.all(outputs["y"] == inp_data) # It's an identity model! + assert np.array_equal(outputs["y"], inp_data) # It's an identity model! + + print("Inference succeeded!") if __name__ == "__main__": diff --git a/tools/Polygraphy/examples/api/04_int8_calibration_in_tensorrt/example.py b/tools/Polygraphy/examples/api/04_int8_calibration_in_tensorrt/example.py index f6c39fd8..008e6e01 100644 --- a/tools/Polygraphy/examples/api/04_int8_calibration_in_tensorrt/example.py +++ b/tools/Polygraphy/examples/api/04_int8_calibration_in_tensorrt/example.py @@ -20,9 +20,7 @@ This script demonstrates how to use the Calibrator API provided by Polygraphy to calibrate a TensorRT engine to run in INT8 precision. """ import numpy as np -from polygraphy.backend.trt import (Calibrator, CreateConfig, - EngineFromNetwork, NetworkFromOnnxPath, - TrtRunner) +from polygraphy.backend.trt import Calibrator, CreateConfig, EngineFromNetwork, NetworkFromOnnxPath, TrtRunner from polygraphy.logger import G_LOGGER @@ -34,7 +32,7 @@ def calib_data(): # (as `int`s) or Polygraphy `DeviceView`s instead of NumPy arrays. # # For details on `DeviceView`, see `polygraphy/cuda/cuda.py`. - yield {"x": np.ones(shape=(1, 1, 2, 2), dtype=np.float32)} # Totally real data + yield {"x": np.ones(shape=(1, 1, 2, 2), dtype=np.float32)} # Totally real data def main(): @@ -46,8 +44,9 @@ def main(): calibrator = Calibrator(data_loader=calib_data(), cache="identity-calib.cache") # We must enable int8 mode in addition to providing the calibrator. - build_engine = EngineFromNetwork(NetworkFromOnnxPath("identity.onnx"), - config=CreateConfig(int8=True, calibrator=calibrator)) + build_engine = EngineFromNetwork( + NetworkFromOnnxPath("identity.onnx"), config=CreateConfig(int8=True, calibrator=calibrator) + ) # When we activate our runner, it will calibrate and build the engine. If we want to # see the logging output from TensorRT, we can temporarily increase logging verbosity: @@ -59,7 +58,7 @@ def main(): # Thus, if you want to store results from multiple inferences, you should use `copy.deepcopy()`. outputs = runner.infer({"x": inp_data}) - assert np.all(outputs["y"] == inp_data) # It's an identity model! + assert np.array_equal(outputs["y"], inp_data) # It's an identity model! if __name__ == "__main__": diff --git a/tools/Polygraphy/examples/api/05_using_tensorrt_network_api/example.py b/tools/Polygraphy/examples/api/05_using_tensorrt_network_api/example.py index 51b6b252..9abe1ce8 100644 --- a/tools/Polygraphy/examples/api/05_using_tensorrt_network_api/example.py +++ b/tools/Polygraphy/examples/api/05_using_tensorrt_network_api/example.py @@ -58,7 +58,9 @@ def main(): # Thus, if you want to store results from multiple inferences, you should use `copy.deepcopy()`. outputs = runner.infer(feed_dict) - assert np.all(outputs[OUTPUT_NAME] == (feed_dict[INPUT_NAME] + 1)) + assert np.array_equal(outputs[OUTPUT_NAME], (feed_dict[INPUT_NAME] + 1)) + + print("Inference succeeded!") if __name__ == "__main__": diff --git a/tools/Polygraphy/examples/api/06_immediate_eval_api/README.md b/tools/Polygraphy/examples/api/06_immediate_eval_api/README.md index 357b1ee1..fdfc86a2 100644 --- a/tools/Polygraphy/examples/api/06_immediate_eval_api/README.md +++ b/tools/Polygraphy/examples/api/06_immediate_eval_api/README.md @@ -50,8 +50,7 @@ config = create_config(builder, network, fp16=True, tf32=True) engine = engine_from_network((builder, network), config) ``` -The script included with this example showcases basic usage of the immediately -evaluated functional API. +`example.py` showcases basic usage of the immediately evaluated functional API. ## Running the Example diff --git a/tools/Polygraphy/examples/api/06_immediate_eval_api/example.py b/tools/Polygraphy/examples/api/06_immediate_eval_api/example.py index c69e4fe3..be1ced43 100644 --- a/tools/Polygraphy/examples/api/06_immediate_eval_api/example.py +++ b/tools/Polygraphy/examples/api/06_immediate_eval_api/example.py @@ -23,9 +23,7 @@ run inference. """ import numpy as np -from polygraphy.backend.trt import (TrtRunner, create_config, - engine_from_network, - network_from_onnx_path) +from polygraphy.backend.trt import TrtRunner, create_config, engine_from_network, network_from_onnx_path def main(): @@ -59,7 +57,9 @@ def main(): # Thus, if you want to store results from multiple inferences, you should use `copy.deepcopy()`. outputs = runner.infer(feed_dict={"x": inp_data}) - assert np.all(outputs["output"] == inp_data) # It's an identity model! + assert np.array_equal(outputs["output"], inp_data) # It's an identity model! + + print("Inference succeeded!") if __name__ == "__main__": diff --git a/tools/Polygraphy/examples/api/07_tensorrt_and_dynamic_shapes/README.md b/tools/Polygraphy/examples/api/07_tensorrt_and_dynamic_shapes/README.md new file mode 100644 index 00000000..593a283e --- /dev/null +++ b/tools/Polygraphy/examples/api/07_tensorrt_and_dynamic_shapes/README.md @@ -0,0 +1,130 @@ +# Using Dynamic Shapes With TensorRT + +## Introduction + +*NOTE: This example is intended for use with TensorRT 8.0 or newer.* + *Older versions may require slight modifications to the example code.* + +In order to use dynamic input shapes with TensorRT, we have to specify a range +(or multiple ranges) of possible shapes when we build the engine. +TensorRT optimization profiles provide the means of doing so. + +Using the TensorRT API, the process involves two steps: + +1. During engine building, specify one or more optimization profiles. + An optimization profile includes 3 shapes for each input: + - `min`: The minimum shape for which the profile should work. + - `opt`: The shape which TensorRT should optimize for. + Generally, you'd want this to correspond to the most commonly used shape. + - `max`: The maximum shape for which the profile should work. + +2. During inference, set the input shape(s) in the execution context, then + query the execution context (*not* the engine) to determine the shape(s) of the output(s). + Based on the output shape(s), the device buffers can be resized to accomodate + the entire output(s). + + For a single-input, single-output model, this would look roughly as follows: + ```python + context.set_binding_shape(0, inp.shape) + + out_shape = context.get_binding_shape(1) + out_buf.resize(out_shape) + + # Rest of inference code... + ``` + +Polygraphy can simplify both steps and help you avoid common pitfalls: + +1. It provides a `Profile` abstraction, which is an `OrderedDict` that + can be converted to a TensorRT `IOptimizationProfile` and includes some utility functions: + - `fill_defaults`: Fills the profile with default shapes based on the network. + - `to_trt`: Creates a TensorRT `IOptimizationProfile` using the shapes in this `Profile`. + + What's more, `Profile` will automatically handle complexities like the + distinction between shape-tensor vs. non-shape-tensor inputs - you do not + need to worry about this distinction yourself. + +2. The `TrtRunner` will automatically handle dynamic shapes in the model. + As in `Profile`, distinctions between shape-tensor and non-shape-tensor inputs + are handled automatically. + + Additionally, the runner will only update the context binding shapes when required, + as changing the shapes has a small overhead. The output device buffers will only + be resized if their current size is smaller that the context outputs, thus avoiding + unnecessary reallocation. + + +### Setting The Stage + +For the sake of this example, we'll imagine a hypothetical scenario: + +We're running an inference workload using an image classification model. + +Normally, we use this model in an online scenario - i.e. we want the lowest possible +latency, so we'll process one image at a time. +For this case, assume `batch_size` is `[1]`. + +However, if we have too many users, then we need to employ dynamic batching so that +our throughput doesn't suffer. Our range of batch sizes is still small to +keep the latency acceptable. Our most frequently used batch size is 4. +For this case, assume `batch_size` is in the range `[1, 32]`. + +In even rarer cases, we need to process large amounts of data offline. In this case, +we use a very large batch size to improve our throughput. +For this case, assume `batch_size` is `[128]`. + +### Performance Considerations + +In implementing our inference pipeline, we need to consider a few tradeoffs: + +- A profile with a large range will not perform as well as for the entire range as + multiple profiles each with smaller ranges. +- Switching shapes within a profile has a small but non-zero cost. +- Switching profiles within a context has a larger cost than switching shapes within a profile. + - We can avoid the cost of switching profiles by creating a separate execution context + for each profile and selecting the appropriate context at runtime. + However, keep in mind that each context will require some additional memory. + + +### A Possible Solution + +Assuming the image size is `(3, 28, 28)`, we'll create three separate +optimization profiles, and a separate context for each: + +1. For the low latency case: + `min=(1, 3, 28, 28), opt=(1, 3, 28, 28), max=(1, 3, 28, 28)` + +2. For the dynamic batching case: + `min=(1, 3, 28, 28), opt=(4, 3, 28, 28), max=(32, 3, 28, 28)` + + Note that we use a batch size of `4` for `opt` since that's the most common case. + +3. For the offline case: + `min=(128, 3, 28, 28), opt=(128, 3, 28, 28), max=(128, 3, 28, 28)` + +For each context, we'll create a corresponding `TrtRunner`. If we make sure that +we own the engine and the context (by not providing them via lazy loaders), then +the cost of activating a runner should be small - it just needs to allocate +input and output buffers. Hence, we'll be able to activate runners on-demand quickly. + + +## Running the Example + +1. Install prerequisites + * Ensure that TensorRT is installed + * Install other dependencies with `python3 -m pip install -r requirements.txt` + +2. Run the example: + ```bash + python3 example.py + ``` + +3. [Optional] Inspect the generated engine: + ```bash + polygraphy inspect model dynamic_identity.engine + ``` + +## Further Reading + +For more information on using dynamic shapes with TensorRT, see the +[developer guide](https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#work_dynamic_shapes) diff --git a/tools/Polygraphy/examples/api/07_tensorrt_and_dynamic_shapes/dynamic_identity.onnx b/tools/Polygraphy/examples/api/07_tensorrt_and_dynamic_shapes/dynamic_identity.onnx new file mode 100644 index 00000000..29df0a4d --- /dev/null +++ b/tools/Polygraphy/examples/api/07_tensorrt_and_dynamic_shapes/dynamic_identity.onnx @@ -0,0 +1,12 @@ +:[ + +XY"Identityonnx_dynamic_identityZ% +X + +  +batch_size + + +b +Y +B diff --git a/tools/Polygraphy/examples/api/07_tensorrt_and_dynamic_shapes/example.py b/tools/Polygraphy/examples/api/07_tensorrt_and_dynamic_shapes/example.py new file mode 100644 index 00000000..8cd5ce9b --- /dev/null +++ b/tools/Polygraphy/examples/api/07_tensorrt_and_dynamic_shapes/example.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. +# +# 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. +# + +""" +This script builds an engine with 3 separate optimization profiles, each +built for a specific use-case. It then creates 3 separate execution contexts +and corresponding `TrtRunner`s for inference. +""" +import numpy as np +from polygraphy.backend.trt import ( + CreateConfig, + NetworkFromOnnxPath, + Profile, + TrtRunner, + engine_from_network, + save_engine, +) +from polygraphy.logger import G_LOGGER + + +def main(): + # A Profile maps each input tensor to a range of shapes. + # + # TIP: To save lines, calls to `add` can be chained: + # profile.add("input0", ...).add("input1", ...) + # + # Of course, you may alternatively write this as: + # profile.add("input0", ...) + # profile.add("input1", ...) + # + profiles = [ + # The low-latency case. For best performance, min == opt == max. + Profile().add("X", min=(1, 3, 28, 28), opt=(1, 3, 28, 28), max=(1, 3, 28, 28)), + # The dynamic batching case. We use `4` for the opt batch size since that's our most common case. + Profile().add("X", min=(1, 3, 28, 28), opt=(4, 3, 28, 28), max=(32, 3, 28, 28)), + # The offline case. For best performance, min == opt == max. + Profile().add("X", min=(128, 3, 28, 28), opt=(128, 3, 28, 28), max=(128, 3, 28, 28)), + ] + + # See examples/api/06_immediate_eval_api for details on immediately evaluated functional loaders like `engine_from_network`. + engine = engine_from_network(NetworkFromOnnxPath("dynamic_identity.onnx"), config=CreateConfig(profiles=profiles)) + + # We'll save the engine so that we can inspect it with `inspect model`. + # This should make it easy to see how the engine bindings are laid out. + save_engine(engine, "dynamic_identity.engine") + + # We'll create, but not activate, three separate runners, each with a separate context. + # + # TIP: By providing a context directly, as opposed to via a lazy loader, + # we can ensure that the runner will *not* take ownership of it. + # + low_latency = TrtRunner(engine.create_execution_context()) + + # NOTE: The following two lines will cause TensorRT to display errors since profile 0 + # is already in use by the first execution context. We'll suppress them using G_LOGGER.verbosity(). + # + with G_LOGGER.verbosity(G_LOGGER.CRITICAL): + dynamic_batching = TrtRunner(engine.create_execution_context()) + offline = TrtRunner(engine.create_execution_context()) + # NOTE: We could update the profile index here (e.g. `context.active_optimization_profile = 2`), + # but instead, we'll use TrtRunner's `set_profile()` API when we later activate the runner. + + # Finally, we can activate the runners as we need them. + # + # NOTE: Since the context and engine are already created, the runner will only need to + # allocate input and output buffers during activation. + + input_img = np.ones((1, 3, 28, 28), dtype=np.float32) # An input "image" + + with low_latency: + outputs = low_latency.infer({"X": input_img}) + assert np.array_equal(outputs["Y"], input_img) # It's an identity model! + + print("Low latency runner succeeded!") + + # While we're serving requests online, we might decide that we need dynamic batching + # for a moment. + # + # NOTE: We're assuming that activating runners will be cheap here, so we can bring up + # the dynamic batching runner just-in-time. + # + # TIP: If activating the runner is not cheap (e.g. input/output buffers are large), + # it might be better to keep the runner active the whole time. + # + with dynamic_batching: + # NOTE: The very first time we activate this runner, we need to set + # the profile index (it's 0 by default). We need to do this *only once*. + # Alternatively, we could have set the profile index in the context directly (see above). + # + dynamic_batching.set_profile(1) # Use the second profile, which is intended for dynamic batching. + + # We'll create fake batches by repeating our fake input image. + small_input_batch = np.repeat(input_img, 4, axis=0) # Shape: (4, 3, 28, 28) + outputs = dynamic_batching.infer({"X": small_input_batch}) + assert np.array_equal(outputs["Y"], small_input_batch) + + # If we need dynamic batching again later, we can activate the runner once more. + # + # NOTE: This time, we do *not* need to set the profile. + # + with dynamic_batching: + # NOTE: We can use any shape that's in the range of the profile without + # additional setup - Polygraphy handles the details behind the scenes! + # + large_input_batch = np.repeat(input_img, 16, axis=0) # Shape: (16, 3, 28, 28) + outputs = dynamic_batching.infer({"X": large_input_batch}) + assert np.array_equal(outputs["Y"], large_input_batch) + + print("Dynamic batching runner succeeded!") + + with offline: + # NOTE: We must set the profile to something other than 0 or 1 since both of those + # are now in use by the `low_latency` and `dynamic_batching` runners respectively. + # + offline.set_profile(2) # Use the third profile, which is intended for the offline case. + + large_offline_batch = np.repeat(input_img, 128, axis=0) # Shape: (128, 3, 28, 28) + outputs = offline.infer({"X": large_offline_batch}) + assert np.array_equal(outputs["Y"], large_offline_batch) + + print("Offline runner succeeded!") + + +if __name__ == "__main__": + main() diff --git a/tools/Polygraphy/examples/cli/convert/01_int8_calibration_in_tensorrt/README.md b/tools/Polygraphy/examples/cli/convert/01_int8_calibration_in_tensorrt/README.md index 44d856f3..f660a134 100644 --- a/tools/Polygraphy/examples/cli/convert/01_int8_calibration_in_tensorrt/README.md +++ b/tools/Polygraphy/examples/cli/convert/01_int8_calibration_in_tensorrt/README.md @@ -1,8 +1,5 @@ # Int8 Calibration In TensorRT - -## Introduction - In [API example 04](../../../api/04_int8_calibration_in_tensorrt/), we saw how we can leverage Polygraphy's included calibrator to easily run int8 calibration with TensorRT. diff --git a/tools/Polygraphy/examples/cli/convert/01_int8_calibration_in_tensorrt/data_loader.py b/tools/Polygraphy/examples/cli/convert/01_int8_calibration_in_tensorrt/data_loader.py index bec51e46..f00cc24d 100644 --- a/tools/Polygraphy/examples/cli/convert/01_int8_calibration_in_tensorrt/data_loader.py +++ b/tools/Polygraphy/examples/cli/convert/01_int8_calibration_in_tensorrt/data_loader.py @@ -24,6 +24,7 @@ import numpy as np INPUT_SHAPE = (1, 1, 2, 2) + def load_data(): for _ in range(5): - yield {"x": np.ones(shape=INPUT_SHAPE, dtype=np.float32)} # Still totally real data + yield {"x": np.ones(shape=INPUT_SHAPE, dtype=np.float32)} # Still totally real data diff --git a/tools/Polygraphy/examples/cli/convert/02_deterministic_engine_builds_in_tensorrt/README.md b/tools/Polygraphy/examples/cli/convert/02_deterministic_engine_builds_in_tensorrt/README.md new file mode 100644 index 00000000..ee22611c --- /dev/null +++ b/tools/Polygraphy/examples/cli/convert/02_deterministic_engine_builds_in_tensorrt/README.md @@ -0,0 +1,47 @@ +# Deterministic Engine Building In TensorRT + + +## Introduction + +During engine building, TensorRT runs and times several kernels in order to select +the most optimal ones. Since kernel timings may vary slightly from run to run, this +process is inherently non-deterministic. + +In many cases, deterministic engine builds may be desirable. One way of achieving this +is to use the `IAlgorithmSelector` API to ensure the same kernels are picked each time. + +To make this process easier, Polygraphy provides two built-in algorithm selectors: +`TacticRecorder` and `TacticReplayer`. The former can be used to record tactics selected +during an engine build, and the latter to play them back during a subsequent build. +The CLI tools include `--save-tactics` and `--load-tactics` options correspnding to these. + +## Running The Example + +1. Build an engine and save a replay file: + + ```bash + polygraphy convert identity.onnx \ + --save-tactics replay.json \ + -o 0.engine + ``` + + The resulting `replay.json` file is human-readable. Optionally, we can + use `inspect tactics` to view it in a friendly format: + + ```bash + polygraphy inspect tactics replay.json + ``` + +2. Use the replay file for another engine build: + + ```bash + polygraphy convert identity.onnx \ + --load-tactics replay.json \ + -o 1.engine + ``` + +3. Verify that the engines are exactly the same: + + ```bash + diff -s 0.engine 1.engine + ``` diff --git a/tools/Polygraphy/examples/cli/run/01_comparing_frameworks/identity.onnx b/tools/Polygraphy/examples/cli/convert/02_deterministic_engine_builds_in_tensorrt/identity.onnx similarity index 100% rename from tools/Polygraphy/examples/cli/run/01_comparing_frameworks/identity.onnx rename to tools/Polygraphy/examples/cli/convert/02_deterministic_engine_builds_in_tensorrt/identity.onnx diff --git a/tools/Polygraphy/examples/cli/convert/03_dynamic_shapes_in_tensorrt/README.md b/tools/Polygraphy/examples/cli/convert/03_dynamic_shapes_in_tensorrt/README.md new file mode 100644 index 00000000..0f547d81 --- /dev/null +++ b/tools/Polygraphy/examples/cli/convert/03_dynamic_shapes_in_tensorrt/README.md @@ -0,0 +1,37 @@ +# Working With Models With Dynamic Shapes In TensorRT + +## Introduction + +In order to use dynamic input shapes with TensorRT, we have to specify a range +(or multiple ranges) of possible shapes when we build the engine. +For details on how this works, refer to +[API example 07](../../../api/07_tensorrt_and_dynamic_shapes/). + +When using the CLI, we can specify the minimum, optimum, and maximum +shapes for each input one or more times. If shapes are specified more than +once per input, multiple optimization profiles are created. + +## Running The Example + +1. Build an engine with 3 separate profiles: + ```bash + polygraphy convert dynamic_identity.onnx -o dynamic_identity.engine \ + --trt-min-shapes X:[1,3,28,28] --trt-opt-shapes X:[1,3,28,28] --trt-max-shapes X:[1,3,28,28] \ + --trt-min-shapes X:[1,3,28,28] --trt-opt-shapes X:[4,3,28,28] --trt-max-shapes X:[32,3,28,28] \ + --trt-min-shapes X:[128,3,28,28] --trt-opt-shapes X:[128,3,28,28] --trt-max-shapes X:[128,3,28,28] + ``` + + For models with multiple inputs, simply provide more arguments to each `--trt-*-shapes` parameter. + For example: `--trt-min-shapes input0:[10,10] input1:[10,10] input2:[10,10] --trt-opt-shapes ...` + + +2. [Optional] Inspect the resulting engine: + ```bash + polygraphy inspect model dynamic_identity.engine + ``` + + +## Further Reading + +For more information on using dynamic shapes with TensorRT, see the +[developer guide](https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#work_dynamic_shapes) diff --git a/tools/Polygraphy/examples/cli/convert/03_dynamic_shapes_in_tensorrt/dynamic_identity.onnx b/tools/Polygraphy/examples/cli/convert/03_dynamic_shapes_in_tensorrt/dynamic_identity.onnx new file mode 100644 index 00000000..29df0a4d --- /dev/null +++ b/tools/Polygraphy/examples/cli/convert/03_dynamic_shapes_in_tensorrt/dynamic_identity.onnx @@ -0,0 +1,12 @@ +:[ + +XY"Identityonnx_dynamic_identityZ% +X + +  +batch_size + + +b +Y +B diff --git a/tools/Polygraphy/examples/cli/inspect/01_inspecting_a_tensorrt_network/README.md b/tools/Polygraphy/examples/cli/inspect/01_inspecting_a_tensorrt_network/README.md index ae10e9d0..d8c3fb3f 100644 --- a/tools/Polygraphy/examples/cli/inspect/01_inspecting_a_tensorrt_network/README.md +++ b/tools/Polygraphy/examples/cli/inspect/01_inspecting_a_tensorrt_network/README.md @@ -23,7 +23,7 @@ This will display something like: {y [dtype=float32, shape=(1, 1, 2, 2)]} ---- 1 Layer(s) ---- - Layer 0 | (Unnamed Layer* 0) [Identity] [Op: LayerType.IDENTITY] + Layer 0 | node_of_y [Op: LayerType.IDENTITY] {x [dtype=float32, shape=(1, 1, 2, 2)]} -> {y [dtype=float32, shape=(1, 1, 2, 2)]} ``` diff --git a/tools/Polygraphy/examples/cli/inspect/02_inspecting_a_tensorrt_engine/README.md b/tools/Polygraphy/examples/cli/inspect/02_inspecting_a_tensorrt_engine/README.md index ab4f109b..f3462db4 100644 --- a/tools/Polygraphy/examples/cli/inspect/02_inspecting_a_tensorrt_engine/README.md +++ b/tools/Polygraphy/examples/cli/inspect/02_inspecting_a_tensorrt_engine/README.md @@ -38,6 +38,7 @@ This will display something like: - Profile: 0 Binding Index: 0 (Input) [Name: X] | Shapes: min=(1, 2, 1, 1), opt=(1, 2, 3, 3), max=(1, 2, 5, 5) Binding Index: 1 (Output) [Name: Y] | Shape: (1, 2, -1, -1) + - Profile: 1 Binding Index: 2 (Input) [Name: X [profile 1]] | Shapes: min=(1, 2, 2, 2), opt=(1, 2, 4, 4), max=(1, 2, 6, 6) Binding Index: 3 (Output) [Name: Y [profile 1]] | Shape: (1, 2, -1, -1) diff --git a/tools/Polygraphy/examples/cli/inspect/05_inspecting_inference_outputs/README.md b/tools/Polygraphy/examples/cli/inspect/05_inspecting_inference_outputs/README.md index 716184bf..103401b2 100644 --- a/tools/Polygraphy/examples/cli/inspect/05_inspecting_inference_outputs/README.md +++ b/tools/Polygraphy/examples/cli/inspect/05_inspecting_inference_outputs/README.md @@ -20,12 +20,10 @@ This will display something like: ``` [I] ==== Run Results (1 runners) ==== - ---- onnxrt-runner-N0-04/23/21-14:37:50 (1 iterations) ---- - y [dtype=float32, shape=(1, 1, 2, 2)] + ---- onnxrt-runner-N0-05/24/21-12:44:30 (1 iterations) ---- + + y [dtype=float32, shape=(1, 1, 2, 2)] | Stats + mean=0.35995, std-dev=0.25784, var=0.066482, median=0.35968, min=0.00011437 at (0, 0, 1, 0), max=0.72032 at (0, 0, 0, 1) [[[[4.17021990e-01 7.20324516e-01] [1.14374816e-04 3.02332580e-01]]]] - - -- Statistics -- - y | Stats - mean=0.35995, std-dev=0.25784, var=0.066482, median=0.35968, min=0.00011437 at (0, 0, 1, 0), max=0.72032 at (0, 0, 0, 1) ``` diff --git a/tools/Polygraphy/examples/cli/inspect/06_inspecting_input_data/README.md b/tools/Polygraphy/examples/cli/inspect/06_inspecting_input_data/README.md index 63917e7c..4ca228c6 100644 --- a/tools/Polygraphy/examples/cli/inspect/06_inspecting_input_data/README.md +++ b/tools/Polygraphy/examples/cli/inspect/06_inspecting_input_data/README.md @@ -20,11 +20,8 @@ This will display something like: ``` [I] ==== Data (1 iterations) ==== - x [dtype=float32, shape=(1, 1, 2, 2)] + x [dtype=float32, shape=(1, 1, 2, 2)] | Stats + mean=0.35995, std-dev=0.25784, var=0.066482, median=0.35968, min=0.00011437 at (0, 0, 1, 0), max=0.72032 at (0, 0, 0, 1) [[[[4.17021990e-01 7.20324516e-01] [1.14374816e-04 3.02332580e-01]]]] - - -- Statistics -- - x | Stats - mean=0.35995, std-dev=0.25784, var=0.066482, median=0.35968, min=0.00011437 at (0, 0, 1, 0), max=0.72032 at (0, 0, 0, 1) ``` diff --git a/tools/Polygraphy/examples/cli/run/01_comparing_frameworks/README.md b/tools/Polygraphy/examples/cli/run/01_comparing_frameworks/README.md index 50e115f9..a5b49744 100644 --- a/tools/Polygraphy/examples/cli/run/01_comparing_frameworks/README.md +++ b/tools/Polygraphy/examples/cli/run/01_comparing_frameworks/README.md @@ -6,5 +6,17 @@ In the simplest case, you can supply a model, and one or more framework flags. For example, to compare an ONNX model between TensorRT and ONNX Runtime: ```bash -polygraphy run identity.onnx --trt --onnxrt +polygraphy run dynamic_identity.onnx --trt --onnxrt ``` + +If our model uses dynamic input shapes, we can specify the shapes to use at +runtime with the `--input-shapes` option: + +```bash +polygraphy run dynamic_identity.onnx --trt --onnxrt \ + --input-shapes X:[1,2,4,4] +``` + +For more details on working with dynamic shapes in TensorRT, refer to +[`convert` example 03](../../convert/03_dynamic_shapes_in_tensorrt/) or +[API example 07](../../../api/07_tensorrt_and_dynamic_shapes/). diff --git a/tools/Polygraphy/examples/cli/run/01_comparing_frameworks/dynamic_identity.onnx b/tools/Polygraphy/examples/cli/run/01_comparing_frameworks/dynamic_identity.onnx new file mode 100644 index 00000000..59f843a7 --- /dev/null +++ b/tools/Polygraphy/examples/cli/run/01_comparing_frameworks/dynamic_identity.onnx @@ -0,0 +1,15 @@ + backend_test:y + +XY"Identityonnx_dynamic_identityZ& +X! + + + +height +widthb& +Y! + + + +height +widthB diff --git a/tools/Polygraphy/examples/cli/run/02_comparing_across_runs/README.md b/tools/Polygraphy/examples/cli/run/02_comparing_across_runs/README.md index 28968aba..3a556309 100644 --- a/tools/Polygraphy/examples/cli/run/02_comparing_across_runs/README.md +++ b/tools/Polygraphy/examples/cli/run/02_comparing_across_runs/README.md @@ -18,5 +18,5 @@ System A to compare against: ```bash polygraphy run identity.onnx --onnxrt \ - --load-results system_a_results.json + --load-outputs system_a_results.json ``` diff --git a/tools/Polygraphy/examples/cli/run/04_defining_a_trt_network_manually/README.md b/tools/Polygraphy/examples/cli/run/04_defining_a_tensorrt_network_manually/README.md similarity index 91% rename from tools/Polygraphy/examples/cli/run/04_defining_a_trt_network_manually/README.md rename to tools/Polygraphy/examples/cli/run/04_defining_a_tensorrt_network_manually/README.md index eb6ec72f..072dfce3 100644 --- a/tools/Polygraphy/examples/cli/run/04_defining_a_trt_network_manually/README.md +++ b/tools/Polygraphy/examples/cli/run/04_defining_a_tensorrt_network_manually/README.md @@ -9,6 +9,9 @@ However, Polygraphy CLI tools provide a work-around for this - if your Python sc named `load_network`, which takes no parameters and returns a TensorRT builder, network, and optionally parser, then you can provide your Python script in place of a model argument. +*TIP: Instead of writing the network script from scratch, you can use `polygraphy template trt-network`* + *to give you a starting point.* + In this example, the included `define_network.py` script parses an ONNX model and appends an identity layer to it. Since it returns the builder, network, and parser in a function called `load_network`, we can build and run a TensorRT engine from it using just a single command: diff --git a/tools/Polygraphy/examples/cli/run/04_defining_a_trt_network_manually/create_config.py b/tools/Polygraphy/examples/cli/run/04_defining_a_tensorrt_network_manually/create_config.py similarity index 100% rename from tools/Polygraphy/examples/cli/run/04_defining_a_trt_network_manually/create_config.py rename to tools/Polygraphy/examples/cli/run/04_defining_a_tensorrt_network_manually/create_config.py diff --git a/tools/Polygraphy/examples/cli/run/04_defining_a_trt_network_manually/define_network.py b/tools/Polygraphy/examples/cli/run/04_defining_a_tensorrt_network_manually/define_network.py similarity index 100% rename from tools/Polygraphy/examples/cli/run/04_defining_a_trt_network_manually/define_network.py rename to tools/Polygraphy/examples/cli/run/04_defining_a_tensorrt_network_manually/define_network.py diff --git a/tools/Polygraphy/examples/cli/run/04_defining_a_trt_network_manually/identity.onnx b/tools/Polygraphy/examples/cli/run/04_defining_a_tensorrt_network_manually/identity.onnx similarity index 100% rename from tools/Polygraphy/examples/cli/run/04_defining_a_trt_network_manually/identity.onnx rename to tools/Polygraphy/examples/cli/run/04_defining_a_tensorrt_network_manually/identity.onnx diff --git a/tools/Polygraphy/polygraphy/README.md b/tools/Polygraphy/polygraphy/README.md index e0216eed..c3d3baf4 100644 --- a/tools/Polygraphy/polygraphy/README.md +++ b/tools/Polygraphy/polygraphy/README.md @@ -22,8 +22,8 @@ may be made in future versions. The Polygraphy API consists broadly of two major components: [`Backend`s](#backends) and the [`Comparator`](#comparator). -**NOTE:** To help you get started with the API, you can use the [`run`](./tools/run/) tool to -with the `--gen-script` auto-generate template scripts that use the Polygraphy API. +**NOTE:** To help you get started with the API, you can use the [`run`](./tools/run/) tool +with the `--gen-script` option to auto-generate template scripts that use the Polygraphy API. Also see the [Polygraphy API documentation](https://docs.nvidia.com/deeplearning/tensorrt/polygraphy/docs/index.html). diff --git a/tools/Polygraphy/polygraphy/__init__.py b/tools/Polygraphy/polygraphy/__init__.py index c69b868b..e87340db 100644 --- a/tools/Polygraphy/polygraphy/__init__.py +++ b/tools/Polygraphy/polygraphy/__init__.py @@ -1 +1,3 @@ -__version__ = "0.29.2" +import polygraphy.config + +__version__ = "0.30.3" diff --git a/tools/Polygraphy/polygraphy/backend/base/loader.py b/tools/Polygraphy/polygraphy/backend/base/loader.py index 1c8f2683..b2f20772 100644 --- a/tools/Polygraphy/polygraphy/backend/base/loader.py +++ b/tools/Polygraphy/polygraphy/backend/base/loader.py @@ -22,6 +22,7 @@ class BaseLoader(object): """ Base class for Polygraphy Loaders. """ + def call_impl(self, *args, **kwargs): """ Implementation for ``__call__``. Derived classes should implement this @@ -29,7 +30,6 @@ class BaseLoader(object): """ raise NotImplementedError("BaseLoader is an abstract class") - @func.constantmethod def __call__(self, *args, **kwargs): """ diff --git a/tools/Polygraphy/polygraphy/backend/base/runner.py b/tools/Polygraphy/polygraphy/backend/base/runner.py index 8686ba2a..ab0c28c6 100644 --- a/tools/Polygraphy/polygraphy/backend/base/runner.py +++ b/tools/Polygraphy/polygraphy/backend/base/runner.py @@ -22,11 +22,13 @@ from polygraphy.logger import G_LOGGER, LogMode np = mod.lazy_import("numpy") + @mod.export() class BaseRunner(object): """ Base class for Polygraphy runners. All runners should override the functions and attributes specified here. """ + RUNNER_COUNTS = defaultdict(int) def __init__(self, name=None, prefix=None): @@ -50,9 +52,6 @@ class BaseRunner(object): self.is_active = False """bool: Whether this runner has been activated, either via context manager, or by calling ``activate()``.""" - self._cached_input_metadata = None - - @func.constantmethod def last_inference_time(self): """ @@ -62,13 +61,15 @@ class BaseRunner(object): float: The time in seconds, or None if runtime was not measured by the runner. """ if self.inference_time is None: - G_LOGGER.warning("{:35} | inference_time was not set. Inference time will be incorrect!" - "To correctly compare runtimes, please set the inference_time property in the" - "infer() function".format(self.name), mode=LogMode.ONCE) + G_LOGGER.warning( + "{:35} | inference_time was not set. Inference time will be incorrect!" + "To correctly compare runtimes, please set the inference_time property in the" + "infer() function".format(self.name), + mode=LogMode.ONCE, + ) return None return self.inference_time - def __enter__(self): """ Activate the runner for inference. This may involve allocating GPU buffers, for example. @@ -76,7 +77,6 @@ class BaseRunner(object): self.activate() return self - def __exit__(self, exc_type, exc_value, traceback): """ Deactivate the runner. @@ -86,7 +86,6 @@ class BaseRunner(object): """ self.deactivate() - def activate_impl(self): """ Implementation for runner activation. Derived classes should override this function @@ -94,7 +93,6 @@ class BaseRunner(object): """ pass - def activate(self): """ Activate the runner for inference. This may involve allocating GPU buffers, for example. @@ -107,8 +105,10 @@ class BaseRunner(object): runner.infer(...) """ if self.is_active: - G_LOGGER.warning("{:35} | Already active; will not activate again. If you really want to " - "activate this runner again, call activate_impl() directly".format(self.name)) + G_LOGGER.warning( + "{:35} | Already active; will not activate again. If you really want to " + "activate this runner again, call activate_impl() directly".format(self.name) + ) return if config.INTERNAL_CORRECTNESS_CHECKS: @@ -117,7 +117,6 @@ class BaseRunner(object): self.activate_impl() self.is_active = True - def infer_impl(self, feed_dict): """ Implementation for runner inference. Derived classes should override this function @@ -125,7 +124,6 @@ class BaseRunner(object): """ raise NotImplementedError("BaseRunner is an abstract class") - def infer(self, feed_dict, check_inputs=True): """ Runs inference using the provided feed_dict. @@ -152,21 +150,26 @@ class BaseRunner(object): input_metadata = self.get_input_metadata() G_LOGGER.verbose("Runner input metadata is: {:}".format(input_metadata)) - util.check_dict_contains(feed_dict, input_metadata.keys(), dict_name="feed_dict", log_func=G_LOGGER.critical) + util.check_dict_contains( + feed_dict, input_metadata.keys(), dict_name="feed_dict", log_func=G_LOGGER.critical + ) for name, inp in feed_dict.items(): meta = input_metadata[name] if not np.issubdtype(inp.dtype, meta.dtype): - G_LOGGER.critical("Input tensor: {:} | Received unexpected dtype: {:}.\n" - "Note: Expected type: {:}".format(name, inp.dtype, meta.dtype)) + G_LOGGER.critical( + "Input tensor: {:} | Received unexpected dtype: {:}.\n" + "Note: Expected type: {:}".format(name, inp.dtype, meta.dtype) + ) if not util.is_valid_shape_override(inp.shape, meta.shape): - G_LOGGER.critical("Input tensor: {:} | Received incompatible shape: {:}.\n" - "Note: Expected a shape compatible with: {:}".format(name, inp.shape, meta.shape)) + G_LOGGER.critical( + "Input tensor: {:} | Received incompatible shape: {:}.\n" + "Note: Expected a shape compatible with: {:}".format(name, inp.shape, meta.shape) + ) return self.infer_impl(feed_dict) - @func.constantmethod def get_input_metadata_impl(self): """ @@ -175,7 +178,6 @@ class BaseRunner(object): """ raise NotImplementedError("BaseRunner is an abstract class") - def get_input_metadata(self): """ Returns information about the inputs of the model. @@ -185,10 +187,7 @@ class BaseRunner(object): Returns: TensorMetadata: Input names, shapes, and data types. """ - if self._cached_input_metadata is None: - self._cached_input_metadata = self.get_input_metadata_impl() - return self._cached_input_metadata - + return self.get_input_metadata_impl() def deactivate_impl(self): """ @@ -197,7 +196,6 @@ class BaseRunner(object): """ pass - def deactivate(self): """ Deactivate the runner. @@ -213,28 +211,29 @@ class BaseRunner(object): runner.infer(...) """ if not self.is_active: - G_LOGGER.warning("{:35} | Not active; will not deactivate. If you really want to " - "deactivate this runner, call deactivate_impl() directly".format(self.name)) + G_LOGGER.warning( + "{:35} | Not active; will not deactivate. If you really want to " + "deactivate this runner, call deactivate_impl() directly".format(self.name) + ) return self.inference_time = None - self._cached_input_metadata = None self.is_active = None try: self.deactivate_impl() except: - raise # Needed so we can have the else clause + raise # Needed so we can have the else clause else: self.is_active = False if config.INTERNAL_CORRECTNESS_CHECKS: old_state = self._pre_activate_runner_state del self._pre_activate_runner_state if old_state != vars(self): - G_LOGGER.internal_error("Runner state was not reset after deactivation. " - "Note:\nOld state: {:}\nNew state: {:}".format(old_state, vars(self))) - - + G_LOGGER.internal_error( + "Runner state was not reset after deactivation. " + "Note:\nOld state: {:}\nNew state: {:}".format(old_state, vars(self)) + ) def __del__(self): if self.is_active: diff --git a/tools/Polygraphy/polygraphy/backend/common/loader.py b/tools/Polygraphy/polygraphy/backend/common/loader.py index bcc88b64..65a4cc3e 100644 --- a/tools/Polygraphy/polygraphy/backend/common/loader.py +++ b/tools/Polygraphy/polygraphy/backend/common/loader.py @@ -22,6 +22,7 @@ class BytesFromPath(BaseLoader): """ Functor that can load a file in binary mode ('rb'). """ + def __init__(self, path): """ Loads a file in binary mode ('rb'). @@ -31,7 +32,6 @@ class BytesFromPath(BaseLoader): """ self._path = path - def call_impl(self): """ Returns: @@ -45,6 +45,7 @@ class SaveBytes(BaseLoader): """ Functor that can save bytes to a file. """ + def __init__(self, obj, path): """ Saves bytes to a file. @@ -56,7 +57,6 @@ class SaveBytes(BaseLoader): self._bytes = obj self._path = path - def call_impl(self): """ Returns: @@ -72,6 +72,7 @@ class InvokeFromScript(BaseLoader): """ Functor that invokes a function from a Python script. """ + def __init__(self, path, name): """ Invokes the specified function from the specified Python script. @@ -86,7 +87,6 @@ class InvokeFromScript(BaseLoader): self._path = path self._name = name - def call_impl(self, *args, **kwargs): """ Returns: diff --git a/tools/Polygraphy/polygraphy/backend/onnx/loader.py b/tools/Polygraphy/polygraphy/backend/onnx/loader.py index 1537ac04..c6389558 100644 --- a/tools/Polygraphy/polygraphy/backend/onnx/loader.py +++ b/tools/Polygraphy/polygraphy/backend/onnx/loader.py @@ -14,6 +14,7 @@ # limitations under the License. # import copy +import os import sys import tempfile @@ -33,13 +34,15 @@ shape_inference = mod.lazy_import("onnx.shape_inference") external_data_helper = mod.lazy_import("onnx.external_data_helper") -LARGE_MODEL_THRESHOLD = (512 << 20) # 512 MiB +LARGE_MODEL_THRESHOLD = 512 << 20 # 512 MiB + class BaseLoadOnnxCopy(BaseLoader): """ Abstract base class for loaders that require loading an ONNX model and potentially making a copy. """ + def __init__(self, model, copy=None): """ Args: @@ -50,7 +53,6 @@ class BaseLoadOnnxCopy(BaseLoader): self._model = model self.copy = util.default(copy, False) - def load(self): model, _ = util.invoke_if_callable(self._model) if self.copy: @@ -65,10 +67,10 @@ class _GSGraphManager(object): If the provided model is already a graph, the graph is not exported to ONNX. """ + def __init__(self, model): self._model = model - def __enter__(self): model, _ = util.invoke_if_callable(self._model) self.USE_GS_GRAPH = isinstance(model, gs.Graph) @@ -78,7 +80,6 @@ class _GSGraphManager(object): self.graph = gs.import_onnx(model) return self - def __exit__(self, exc_type, exc_value, traceback): if self.USE_GS_GRAPH: self.retval = self.graph @@ -91,6 +92,7 @@ class OnnxFromPath(BaseLoader): """ Functor that loads an ONNX model from a file. """ + def __init__(self, path, external_data_dir=None): """ Loads an ONNX model from a file. @@ -103,7 +105,6 @@ class OnnxFromPath(BaseLoader): self.path = path self.external_data_dir = external_data_dir - def call_impl(self): """ Returns: @@ -113,6 +114,7 @@ class OnnxFromPath(BaseLoader): # If external_data_dir is not None, we'll load external data ourselves model = onnx.load(self.path, load_external_data=self.external_data_dir is None) if self.external_data_dir is not None: + G_LOGGER.verbose("Loading external data from: {:}".format(self.external_data_dir)) external_data_helper.load_external_data_for_model(model, self.external_data_dir) return model @@ -122,6 +124,7 @@ class OnnxFromTfGraph(BaseLoader): """ Functor that loads a TensorFlow graph and converts it to ONNX using the tf2onnx converter. """ + def __init__(self, graph, opset=None, optimize=None, fold_constant=None): """ Converts a TensorFlow model into ONNX. @@ -145,8 +148,9 @@ class OnnxFromTfGraph(BaseLoader): self.optimize = util.default(optimize, True) if self.fold_constant and not self.optimize: - G_LOGGER.warning("`fold_constant` is enabled, but `optimize` is disabled. Constant folding will not be performed") - + G_LOGGER.warning( + "`fold_constant` is enabled, but `optimize` is disabled. Constant folding will not be performed" + ) def call_impl(self): """ @@ -160,23 +164,28 @@ class OnnxFromTfGraph(BaseLoader): G_LOGGER.info("Folding constants in graph using tf2onnx.tfonnx.tf_optimize") graphdef = graph.as_graph_def() if self.optimize: - graphdef = tf2onnx.tfonnx.tf_optimize(input_names, output_names, graph.as_graph_def(), fold_constant=self.fold_constant) + graphdef = tf2onnx.tfonnx.tf_optimize( + input_names, output_names, graph.as_graph_def(), fold_constant=self.fold_constant + ) with tf.Graph().as_default() as graph, tf.compat.v1.Session(graph=graph) as sess: tf.import_graph_def(graphdef, name="") - onnx_graph = tf2onnx.tfonnx.process_tf_graph(graph, input_names=input_names, output_names=output_names, opset=self.opset) + onnx_graph = tf2onnx.tfonnx.process_tf_graph( + graph, input_names=input_names, output_names=output_names, opset=self.opset + ) if self.optimize: onnx_graph = tf2onnx.optimizer.optimize_graph(onnx_graph) return onnx_graph.make_model("model") -@mod.export_deprecated_alias("ModifyOnnx", remove_in="0.30.0") +@mod.export_deprecated_alias("ModifyOnnx", remove_in="0.32.0") @mod.export(funcify=True) class ModifyOutputs(BaseLoadOnnxCopy): """ Functor that modifies the outputs of an ONNX model. """ + def __init__(self, model, outputs=None, exclude_outputs=None, copy=None): """ Modifies outputs of an ONNX model. @@ -197,7 +206,6 @@ class ModifyOutputs(BaseLoadOnnxCopy): self.outputs = outputs self.exclude_outputs = exclude_outputs - def call_impl(self): """ Returns: @@ -223,6 +231,7 @@ class ConvertToFp16(BaseLoadOnnxCopy): Functor that converts all floating point tensors in the model to 16-bit precision. This is *not* needed in order to use TensorRT's fp16 precision, but may be useful for other backends. """ + def __init__(self, model, copy=None): """ Converts all floating point tensors in the model to 16-bit precision. @@ -233,7 +242,6 @@ class ConvertToFp16(BaseLoadOnnxCopy): """ super().__init__(model, copy) - def call_impl(self): """ Returns: @@ -243,9 +251,10 @@ class ConvertToFp16(BaseLoadOnnxCopy): G_LOGGER.info("Converting float tensors to float16") try: - model = onnxmltools.utils.float16_converter.convert_float_to_float16(model, keep_io_types=True, - disable_shape_inference=True) - except TypeError: # Using an old version of onnxmltools + model = onnxmltools.utils.float16_converter.convert_float_to_float16( + model, keep_io_types=True, disable_shape_inference=True + ) + except TypeError: # Using an old version of onnxmltools model = onnxmltools.utils.float16_converter.convert_float_to_float16(model) return model @@ -256,8 +265,17 @@ class FoldConstants(BaseLoadOnnxCopy): """ Functor that folds constants in an ONNX model. """ - def __init__(self, model, num_passes=None, do_shape_inference=None, partitioning=None, - fold_shapes=None, copy=None, error_ok=None): + + def __init__( + self, + model, + num_passes=None, + do_shape_inference=None, + partitioning=None, + fold_shapes=None, + copy=None, + error_ok=None, + ): """ Fold constants in an ONNX model. @@ -267,17 +285,19 @@ class FoldConstants(BaseLoadOnnxCopy): num_passes (int): The number of constant folding passes to run. Sometimes, subgraphs that compute tensor shapes may not be foldable in a single pass. - If not specified, Polygraphy will automatically determine the number of passes required. + By default, Polygraphy will automatically determine the number of passes required. do_shape_inference (bool): Whether to run shape inference in the model between passes. This enables the loader to fold `Shape` nodes. + Only effective if `fold_shapes` is True. + Defaults to True. partitioning (Union[str, None]): Whether/How to partition the graph so that errors in folding one part of a model do not affect other parts. Available modes are: - None: Do not partition the graph. If inference fails, no constants are folded. - 'basic': Partition the graph. If inference fails in one partition, other partitions will remain unaffected. - - 'recursive': Parition the graph recursively. If inference fails in a partition, the partition will be further paritioned. + - 'recursive': Parition the graph recursively. If inference fails in a partition, the partition will be further partitioned. Defaults to None. fold_shapes (bool): @@ -286,7 +306,8 @@ class FoldConstants(BaseLoadOnnxCopy): static shapes. Defaults to True. copy (bool): - Whether to create a copy of the model first. Defaults to False. + Whether to create a copy of the model first. + Defaults to False. error_ok (bool): Whether to suppress errors during constant folding. If this is set to `False`, errors will be re-raised. @@ -299,42 +320,46 @@ class FoldConstants(BaseLoadOnnxCopy): self.fold_shapes = util.default(fold_shapes, True) self.error_ok = util.default(error_ok, True) - def call_impl(self): """ Returns: onnx.ModelProto: The new ONNX model with constants folded. """ + def run_const_fold_pass(model): graph = gs.import_onnx(model) del model try: graph.fold_constants(fold_shapes=self.fold_shapes, partitioning=self.partitioning) - except TypeError as err: # Using an old version of ONNX-GS + except TypeError as err: # Using an old version of ONNX-GS if self.partitioning: - G_LOGGER.critical("This version of ONNX-GraphSurgeon may not support partitioning the graph. " - "Please upgrade to a newer version of ONNX-GraphSurgeon or disable partitioning.\n" - "Note: Error was:\n{:}".format(err)) + G_LOGGER.critical( + "This version of ONNX-GraphSurgeon may not support partitioning the graph.\n" + "Please upgrade to a newer version of ONNX-GraphSurgeon or disable partitioning.\n" + "Note: Error was:\n{:}".format(err) + ) if self.fold_shapes: - G_LOGGER.critical("This version of ONNX-GraphSurgeon may not support folding shapes. " - "Please upgrade to a newer version of ONNX-GraphSurgeon or disable shape folding.\n" - "Note: Error was:\n{:}".format(err)) + G_LOGGER.critical( + "This version of ONNX-GraphSurgeon may not support folding shapes.\n" + "Please upgrade to a newer version of ONNX-GraphSurgeon or disable shape folding.\n" + "Note: Error was:\n{:}".format(err) + ) graph.fold_constants() - model = gs.export_onnx(graph.cleanup(), - do_type_check=False) + model = gs.export_onnx(graph.cleanup(), do_type_check=False) del graph - if self.do_shape_inference: + if self.fold_shapes and self.do_shape_inference: model = infer_shapes(model) return model - if not mod.has_mod(onnxrt, "__version__"): - G_LOGGER.error("ONNX-Runtime is not installed, constant folding may not work.\n" - "Consider installing ONNX-Runtime: {:} -m pip install onnxruntime".format(sys.executable)) + G_LOGGER.error( + "ONNX-Runtime is not installed, so constant folding may be suboptimal or not work at all.\n" + "Consider installing ONNX-Runtime: {:} -m pip install onnxruntime".format(sys.executable) + ) model = self.load() @@ -351,15 +376,19 @@ class FoldConstants(BaseLoadOnnxCopy): except Exception as err: if not self.error_ok: raise - G_LOGGER.warning("Constant folding pass failed. Skipping subsequent passes.\n" - "Note: Error was:\n{:}".format(err)) + G_LOGGER.warning( + "Constant folding pass failed. Skipping subsequent passes.\n" "Note: Error was:\n{:}".format(err) + ) break else: postfold_num_nodes = onnx_util.get_num_nodes(model) index += 1 - G_LOGGER.finish("\tTotal Nodes | Original: {:5}, After Folding: {:5} | {:5} Nodes Folded".format( - prefold_num_nodes, postfold_num_nodes, prefold_num_nodes - postfold_num_nodes)) + G_LOGGER.finish( + "\tTotal Nodes | Original: {:5}, After Folding: {:5} | {:5} Nodes Folded".format( + prefold_num_nodes, postfold_num_nodes, prefold_num_nodes - postfold_num_nodes + ) + ) return model @@ -369,18 +398,32 @@ class InferShapes(BaseLoader): """ Functor that runs shape inference on an ONNX model. """ - def __init__(self, model, error_ok=None): + + def __init__(self, model, error_ok=None, external_data_dir=None, save_to_disk_threshold_bytes=None): """ Run shape inference on an ONNX model. Args: - model (Callable() -> onnx.ModelProto): A loader that can supply an ONNX model. + model (Callable() -> onnx.ModelProto): + A loader that can supply an ONNX model, or a path to a model. + Supports models larger than the 2 GiB protobuf limit. - error_ok (bool): Whether errors during shape inference should be suppressed. Defaults to True. + error_ok (bool): + Whether errors during shape inference should be suppressed. Defaults to True. + external_data_dir (str): + The directory where external data for the model is stored. + Only used if the model is provided via a path rather than a loader. + save_to_disk_threshold_bytes (int): + The size in bytes above which a ModelProto will be serialized to the disk + before running shape inference. + This can be used to work around the 2 GiB protobuf limitation. + Defaults to ~2 GiB. """ self._model = model self.error_ok = util.default(error_ok, True) - + self.external_data_dir = external_data_dir + # Subtract a little so we're below the real threshold + self.save_to_disk_threshold_bytes = util.default(save_to_disk_threshold_bytes, (2 << 30) - 8192) def call_impl(self): """ @@ -388,24 +431,53 @@ class InferShapes(BaseLoader): onnx.ModelProto: The new ONNX model with shapes inferred. """ model, _ = util.invoke_if_callable(self._model) + external_data_dir = self.external_data_dir - G_LOGGER.verbose("Starting ONNX shape inference") try: if isinstance(model, onnx.ModelProto): - if model.ByteSize() > LARGE_MODEL_THRESHOLD: - G_LOGGER.warning("Attempting to run shape inference on a large model. " - "This may require a large amount of memory.\nIf memory consumption becomes too high, " - "the process may be killed. You may want to try disabling shape inference in that case. ", mode=LogMode.ONCE) + MODEL_SIZE = model.ByteSize() + if MODEL_SIZE > LARGE_MODEL_THRESHOLD: + G_LOGGER.warning( + "Attempting to run shape inference on a large model. " + "This may require a large amount of memory.\nIf memory consumption becomes too high, " + "the process may be killed. You may want to try disabling shape inference in that case. ", + mode=LogMode.ONCE, + ) + + if MODEL_SIZE > self.save_to_disk_threshold_bytes: + G_LOGGER.warning( + "Model size ({:.3} MiB) exceeds the in-memory size threshold: {:.3} MiB.\n" + "The model will be saved to a temporary file before shape inference is run.".format( + MODEL_SIZE / (1024.0 ** 2), self.save_to_disk_threshold_bytes / (1024.0 ** 2) + ), + mode=LogMode.ONCE, + ) + outdir = tempfile.TemporaryDirectory() + outpath = os.path.join(outdir.name, "tmp_model.onnx") + save_onnx(model, outpath, external_data_path="ext.data") + model = outpath + external_data_dir = outdir.name + + G_LOGGER.verbose("Starting ONNX shape inference") + if isinstance(model, onnx.ModelProto): model = shape_inference.infer_shapes(model) else: - with tempfile.NamedTemporaryFile(suffix=".onnx") as f: + with tempfile.NamedTemporaryFile(prefix="tmp_polygraphy_", suffix=".onnx") as f: + G_LOGGER.verbose("Writing shape-inferred model to: {:}".format(f.name)) shape_inference.infer_shapes_path(model, f.name) - model = onnx_from_path(f.name) + # When external_data_dir is unset, use the model's current directory + model = onnx_from_path( + f.name, external_data_dir=util.default(external_data_dir, os.path.dirname(model) or None) + ) G_LOGGER.verbose("ONNX Shape Inference completed successfully") except Exception as err: if not self.error_ok: raise G_LOGGER.warning("ONNX shape inference exited with an error:\n{:}".format(err)) + G_LOGGER.internal_error("ONNX shape inference exited with an error:\n{:}".format(err)) + + if not isinstance(model, onnx.ModelProto): + model = onnx_from_path(model, external_data_dir=self.external_data_dir) return model @@ -414,6 +486,7 @@ class ExtractSubgraph(BaseLoader): """ Functor that extracts a subgraph from an ONNX model. """ + def __init__(self, model, input_metadata=None, output_metadata=None, check_meta=None): """ Extracts a subgraph from an ONNX model. @@ -440,7 +513,6 @@ class ExtractSubgraph(BaseLoader): self.output_metadata = output_metadata self.check_meta = util.default(check_meta, True) - def call_impl(self): """ Returns: @@ -453,26 +525,27 @@ class ExtractSubgraph(BaseLoader): def get_tensor(name): if name not in TENSOR_MAP: - G_LOGGER.exit("Tensor: {:} does not exist in the model.".format(name)) + G_LOGGER.critical("Tensor: {:} does not exist in the model.".format(name)) return TENSOR_MAP[name] - def update_tensor(name, dtype, shape): tensor = get_tensor(name) tensor.dtype, tensor.shape = dtype or tensor.dtype, shape or tensor.shape return tensor - def check_meta(name, dtype, shape, meta_type, needs_shape=True): if not self.check_meta: return if needs_shape and shape is None: - G_LOGGER.warning("{:} metadata should include shape, but no shape was " - "provided for tensor: {:}".format(meta_type, name)) + G_LOGGER.warning( + "{:} metadata should include shape, but no shape was " + "provided for tensor: {:}".format(meta_type, name) + ) if dtype is None: - G_LOGGER.warning("{:} metadata should include data type, but no data type was " - "provided for tensor: {:}".format(meta_type, name)) - + G_LOGGER.warning( + "{:} metadata should include data type, but no data type was " + "provided for tensor: {:}".format(meta_type, name) + ) if self.input_metadata is not None: graph.inputs.clear() @@ -499,21 +572,37 @@ class SaveOnnx(BaseLoader): """ Functor that saves an ONNX model to the specified path. """ - def __init__(self, model, path, external_data_path=None, size_threshold=None): + + def __init__(self, model, path, external_data_path=None, size_threshold=None, all_tensors_to_one_file=None): """ Saves an ONNX model to the specified path. Args: model (Callable() -> onnx.ModelProto): A loader that can supply an ONNX model. path (str): Path at which to write the ONNX model. - external_data_path (str): Path to save external data. - size_threshold (int): Tensor size threshold for storing tensor data in the external file. - """ + external_data_path (str): + Path to save external data. + This is always a relative path; external data is always written to the same + directory as the model. + Set to an empty string to use the default path. + Set to None to disable. + Defaults to None. + size_threshold (int): + Tensor size threshold, in bytes, above which tensor data will be + stored in the external file. + Tensors smaller that this threshold will remain in the ONNX file. + Has no effect if external_data_path is not set. + Defaults to 1024. + all_tensors_to_one_file (bool): + Whether to write all tensors to one file when saving external data. + Has no effect if external_data_path is not set. + Defaults to True. + """ self._model = model self.path = path self.external_data_path = external_data_path self.size_threshold = size_threshold - + self.all_tensors_to_one_file = all_tensors_to_one_file def call_impl(self): """ @@ -523,13 +612,35 @@ class SaveOnnx(BaseLoader): model, _ = util.invoke_if_callable(self._model) G_LOGGER.info("Saving ONNX model to: {:}".format(self.path)) if self.external_data_path is not None: + G_LOGGER.verbose("Saving external data for ONNX model to: {:}".format(self.external_data_path)) try: - external_data_helper.convert_model_to_external_data(model, location=self.external_data_path, - size_threshold=util.default(self.size_threshold, 0)) + external_data_helper.convert_model_to_external_data( + model, + location=self.external_data_path, + all_tensors_to_one_file=util.default(self.all_tensors_to_one_file, True), + size_threshold=util.default(self.size_threshold, 1024), + ) except TypeError: if self.size_threshold is not None: - G_LOGGER.warning("This version of onnx does not support size_threshold in convert_model_to_external_data") - external_data_helper.convert_model_to_external_data(model, location=self.external_data_path) + G_LOGGER.warning( + "This version of onnx does not support size_threshold in convert_model_to_external_data" + ) + external_data_helper.convert_model_to_external_data( + model, + location=self.external_data_path, + all_tensors_to_one_file=util.default(self.all_tensors_to_one_file, True), + ) + else: + if self.size_threshold is not None: + G_LOGGER.warning( + "size_threshold is set, but external data path has not been set. " + "No external data will be written." + ) + if self.all_tensors_to_one_file is not None: + G_LOGGER.warning( + "all_tensors_to_one_file is set, but external data path has not been set. " + "No external data will be written." + ) onnx.save(model, self.path) return model @@ -540,16 +651,16 @@ class BytesFromOnnx(BaseLoader): """ Functor that serializes an ONNX model. """ + def __init__(self, model): """ Serializes an ONNX model. Args: model (Callable() -> onnx.ModelProto): A loader that can supply an ONNX model. - """ + """ self._model = model - def call_impl(self): """ Returns: diff --git a/tools/Polygraphy/polygraphy/backend/onnx/util.py b/tools/Polygraphy/polygraphy/backend/onnx/util.py index d6de6882..ac24be64 100644 --- a/tools/Polygraphy/polygraphy/backend/onnx/util.py +++ b/tools/Polygraphy/polygraphy/backend/onnx/util.py @@ -47,8 +47,12 @@ def all_tensor_names(model): def check_outputs_not_found(not_found, all_outputs): if not_found: - G_LOGGER.critical("The following outputs: {:} were not found. " - "Note: Available tensors: {:}".format(not_found, all_outputs)) + G_LOGGER.critical( + "The following outputs were not found: {:}.\nNote: Available tensors:\n\t{:}".format( + not_found, "\n\t".join(all_outputs) + ) + ) + def mark_outputs(model, outputs): # Clear the old outputs @@ -85,7 +89,7 @@ def unmark_outputs(model, outputs): cur_outputs = [] while model.graph.output: cur_outputs.append(model.graph.output.pop()) - cur_outputs = list(reversed(cur_outputs)) # Preserve ordering + cur_outputs = list(reversed(cur_outputs)) # Preserve ordering unmarked_outputs = set() for out in cur_outputs: @@ -135,7 +139,7 @@ def get_tensor_metadata(tensors): def get_input_metadata(graph): # Some "inputs" are actually weights with initalizers, so we need to eliminate those. - initializer_names = set([tensor.name for tensor in graph.initializer]) + initializer_names = {tensor.name for tensor in graph.initializer} input_tensors = [tensor for tensor in graph.input if tensor.name not in initializer_names] return get_tensor_metadata(input_tensors) @@ -155,6 +159,7 @@ def str_from_onnx(model, mode="full"): Returns: str """ + def get_opset(): try: return model.opset_import[0].version @@ -192,7 +197,8 @@ def str_from_onnx_graph(graph, mode, tensors, indent_level=0): if mode == "full": for init in graph.initializer: onnx_str += "Initializer | {:} [dtype={:}, shape={:}] | Values:\n{:}\n\n".format( - init.name, get_dtype(init), get_shape(init), util.indent_block(str(get_values(init)))) + init.name, get_dtype(init), get_shape(init), util.indent_block(str(get_values(init))) + ) if not graph.initializer: onnx_str += "{}\n\n" elif mode != "none": @@ -201,7 +207,6 @@ def str_from_onnx_graph(graph, mode, tensors, indent_level=0): else: onnx_str += "\n" - def metadata_from_names(names): metadata = TensorMetadata() for name in names: @@ -229,6 +234,7 @@ def str_from_onnx_graph(graph, mode, tensors, indent_level=0): def attrs_to_dict(attrs): attr_dict = OrderedDict() for attr in attrs: + def process_attr(attr_str: str): processed = getattr(attr, ONNX_PYTHON_ATTR_MAPPING[attr_str]) if attr_str == "STRING": @@ -252,13 +258,16 @@ def str_from_onnx_graph(graph, mode, tensors, indent_level=0): if attr_str in ONNX_PYTHON_ATTR_MAPPING: attr_dict[attr.name] = process_attr(attr_str) else: - G_LOGGER.warning("Attribute of type {:} is currently unsupported. Skipping attribute.".format(attr_str)) + G_LOGGER.warning( + "Attribute of type {:} is currently unsupported. Skipping attribute.".format(attr_str) + ) else: - G_LOGGER.warning("Attribute type: {:} was not recognized. Was the graph generated with a newer IR " - "version than the installed `onnx` package? Skipping attribute.".format(attr.type)) + G_LOGGER.warning( + "Attribute type: {:} was not recognized. Was the graph generated with a newer IR " + "version than the installed `onnx` package? Skipping attribute.".format(attr.type) + ) return attr_dict - onnx_str += "---- {:} Node(s) ----\n".format(len(graph.node)) if mode != "none": for index, node in enumerate(graph.node): diff --git a/tools/Polygraphy/polygraphy/backend/onnxrt/loader.py b/tools/Polygraphy/polygraphy/backend/onnxrt/loader.py index 352998f5..cd80ca59 100644 --- a/tools/Polygraphy/polygraphy/backend/onnxrt/loader.py +++ b/tools/Polygraphy/polygraphy/backend/onnxrt/loader.py @@ -19,12 +19,13 @@ from polygraphy.backend.base import BaseLoader onnxruntime = mod.lazy_import("onnxruntime") -@mod.export_deprecated_alias("SessionFromOnnxBytes", remove_in="0.30.0") +@mod.export_deprecated_alias("SessionFromOnnxBytes", remove_in="0.32.0") @mod.export(funcify=True) class SessionFromOnnx(BaseLoader): """ Functor that builds an ONNX-Runtime inference session. """ + def __init__(self, model_bytes): """ Builds an ONNX-Runtime inference session. @@ -35,7 +36,6 @@ class SessionFromOnnx(BaseLoader): """ self._model_bytes_or_path = model_bytes - def call_impl(self): """ Returns: diff --git a/tools/Polygraphy/polygraphy/backend/onnxrt/runner.py b/tools/Polygraphy/polygraphy/backend/onnxrt/runner.py index 3a1f0d19..d5f3a8ec 100644 --- a/tools/Polygraphy/polygraphy/backend/onnxrt/runner.py +++ b/tools/Polygraphy/polygraphy/backend/onnxrt/runner.py @@ -28,24 +28,25 @@ class OnnxrtRunner(BaseRunner): """ Runs inference using an ONNX-Runtime inference session. """ + def __init__(self, sess, name=None): """ Args: sess (Callable() -> onnxruntime.InferenceSession): - A callable that can supply an ONNX-Runtime inferences session. + A callable that can supply an ONNX-Runtime inference session. + This callable is invoked whenever the runner is activated. + + Alternatively, the inference session may be supplied directly. """ super().__init__(name=name, prefix="onnxrt-runner") self._sess = sess - def activate_impl(self): self.sess, _ = util.invoke_if_callable(self._sess) - def deactivate_impl(self): del self.sess - def infer_impl(self, feed_dict): start = time.time() inference_outputs = self.sess.run(None, feed_dict) @@ -57,7 +58,6 @@ class OnnxrtRunner(BaseRunner): self.inference_time = end - start return out_dict - @func.constantmethod def get_input_metadata_impl(self): ONNX_RT_TYPE_TO_NP = { diff --git a/tools/Polygraphy/polygraphy/backend/pyt/runner.py b/tools/Polygraphy/polygraphy/backend/pyt/runner.py index 33ee99e2..edafee60 100644 --- a/tools/Polygraphy/polygraphy/backend/pyt/runner.py +++ b/tools/Polygraphy/polygraphy/backend/pyt/runner.py @@ -27,6 +27,7 @@ class PytRunner(BaseRunner): """ Runs inference using PyTorch. """ + def __init__(self, model, input_metadata, output_names, name=None): """ Args: @@ -47,15 +48,16 @@ class PytRunner(BaseRunner): self.input_metadata = input_metadata self.output_names = output_names - def activate_impl(self): self.model, _ = util.invoke_if_callable(self._model) self.model.eval() - def infer_impl(self, feed_dict): with torch.no_grad(): - inputs = [torch.from_numpy(val.astype(dtype)).cuda() for (val, (dtype, _)) in zip(feed_dict.values(), self.input_metadata.values())] + inputs = [ + torch.from_numpy(val.astype(dtype)).cuda() + for (val, (dtype, _)) in zip(feed_dict.values(), self.input_metadata.values()) + ] start = time.time() outputs = self.model(*inputs) end = time.time() @@ -65,11 +67,9 @@ class PytRunner(BaseRunner): out_dict[name] = output.cpu().numpy() return out_dict, end - start - def deactivate_impl(self): del self.model - @func.constantmethod def get_input_metadata_impl(self): return self.input_metadata diff --git a/tools/Polygraphy/polygraphy/backend/tf/README.md b/tools/Polygraphy/polygraphy/backend/tf/README.md new file mode 100644 index 00000000..b52e90a2 --- /dev/null +++ b/tools/Polygraphy/polygraphy/backend/tf/README.md @@ -0,0 +1 @@ +NOTE: The `tf` backend currently only supports TensorFlow 1.X. diff --git a/tools/Polygraphy/polygraphy/backend/tf/__init__.py b/tools/Polygraphy/polygraphy/backend/tf/__init__.py index d709308d..c48a6e5a 100644 --- a/tools/Polygraphy/polygraphy/backend/tf/__init__.py +++ b/tools/Polygraphy/polygraphy/backend/tf/__init__.py @@ -27,8 +27,9 @@ def register_logger_callback(): tf_logging_level = "0" tf.compat.v1.logging.set_verbosity(tf_sev) - os.environ['TF_CPP_MIN_LOG_LEVEL'] = tf_logging_level + os.environ["TF_CPP_MIN_LOG_LEVEL"] = tf_logging_level + + G_LOGGER.register_callback(set_tf_logging_level) # Will be registered when this runner is imported. - G_LOGGER.register_callback(set_tf_logging_level) # Will be registered when this runner is imported. register_logger_callback() diff --git a/tools/Polygraphy/polygraphy/backend/tf/loader.py b/tools/Polygraphy/polygraphy/backend/tf/loader.py index 802d22e8..172fc839 100644 --- a/tools/Polygraphy/polygraphy/backend/tf/loader.py +++ b/tools/Polygraphy/polygraphy/backend/tf/loader.py @@ -23,11 +23,13 @@ from polygraphy.logger import G_LOGGER tf = mod.lazy_import("tensorflow", version="<2.0") + @mod.export(funcify=True) class OptimizeGraph(BaseLoader): """ Functor that freezes a TensorFlow graph, and folds constants. """ + def __init__(self, graph): """ Freezes a TensorFlow graph and folds constants. @@ -38,10 +40,8 @@ class OptimizeGraph(BaseLoader): """ self._graph = graph - def constfold(self, graphdef, output_names): - from tensorflow.core.protobuf import (config_pb2, meta_graph_pb2, - rewriter_config_pb2) + from tensorflow.core.protobuf import config_pb2, meta_graph_pb2, rewriter_config_pb2 from tensorflow.python.framework import importer, ops from tensorflow.python.grappler import tf_optimizer from tensorflow.python.training import saver @@ -59,13 +59,12 @@ class OptimizeGraph(BaseLoader): rewriter_config = rewriter_config_pb2.RewriterConfig() rewriter_config.optimizers.extend(["constfold"]) - rewriter_config.meta_optimizer_iterations = (rewriter_config_pb2.RewriterConfig.ONE) + rewriter_config.meta_optimizer_iterations = rewriter_config_pb2.RewriterConfig.ONE session_config = config_pb2.ConfigProto() session_config.graph_options.resave_options.CopyFrom(rewriter_config) return tf_optimizer.OptimizeGraph(session_config, metagraph, graph_id=b"graph") - def call_impl(self): """ Returns: @@ -81,21 +80,25 @@ class OptimizeGraph(BaseLoader): G_LOGGER.ultra_verbose("Removed nodes: {:}".format(removed)) for node in graphdef.node: - if node.op == 'RefSwitch': - node.op = 'Switch' + if node.op == "RefSwitch": + node.op = "Switch" for index in range(len(node.input)): - if 'moving_' in node.input[index]: - node.input[index] = node.input[index] + '/read' - elif node.op == 'AssignSub': - node.op = 'Sub' - if 'use_locking' in node.attr: del node.attr['use_locking'] - elif node.op == 'AssignAdd': - node.op = 'Add' - if 'use_locking' in node.attr: del node.attr['use_locking'] - elif node.op == 'Assign': - node.op = 'Identity' - if 'use_locking' in node.attr: del node.attr['use_locking'] - if 'validate_shape' in node.attr: del node.attr['validate_shape'] + if "moving_" in node.input[index]: + node.input[index] = node.input[index] + "/read" + elif node.op == "AssignSub": + node.op = "Sub" + if "use_locking" in node.attr: + del node.attr["use_locking"] + elif node.op == "AssignAdd": + node.op = "Add" + if "use_locking" in node.attr: + del node.attr["use_locking"] + elif node.op == "Assign": + node.op = "Identity" + if "use_locking" in node.attr: + del node.attr["use_locking"] + if "validate_shape" in node.attr: + del node.attr["validate_shape"] if len(node.input) == 2: # input0: ref: Should be from a Variable node. May be uninitialized. # input1: value: The value to be assigned to the variable. @@ -114,6 +117,7 @@ class GraphFromKeras(BaseLoader): """ Functor that loads a TensorFlow model from Keras. """ + def __init__(self, path): """ Loads a TensorFlow model from Keras. @@ -123,7 +127,6 @@ class GraphFromKeras(BaseLoader): """ self.path = path - def call_impl(self): """ Returns: @@ -143,6 +146,7 @@ class GraphFromFrozen(BaseLoader): """ Functor that loads a TensorFlow frozen model. """ + def __init__(self, path): """ Loads a TensorFlow frozen model. @@ -153,7 +157,6 @@ class GraphFromFrozen(BaseLoader): """ self.path = path - def call_impl(self): """ Returns: @@ -169,6 +172,7 @@ class GraphFromCkpt(BaseLoader): Functor that loads a TensorFlow model from a checkpoint. Note that in order to use checkpoints, you must NOT use subprocesses in the Comparator. """ + def __init__(self, dir, name=None): """ Loads a TensorFlow model from a checkpoint. @@ -184,7 +188,6 @@ class GraphFromCkpt(BaseLoader): self.dir = dir self.name = name - def call_impl(self): """ Returns: @@ -204,15 +207,17 @@ class GraphFromCkpt(BaseLoader): checkpoint = tf.train.get_checkpoint_state(self.dir) if checkpoint is None: ckpt_file_contents = '\nmodel_checkpoint_path: "model"\nall_model_checkpoint_paths: "model"\n' - G_LOGGER.critical("Checkpoint directory: {:} does not contain a `checkpoint` file, and the checkpoint name was " - "not provided. Please either create a checkpoint file with the contents:\n{:} " - "\nWhere `model` is the name of the checkpoint, or explicitly provide the name with " - "--ckpt, not including file extensions".format(self.dir, ckpt_file_contents)) + G_LOGGER.critical( + "Checkpoint directory: {:} does not contain a `checkpoint` file, and the checkpoint name was " + "not provided. Please either create a checkpoint file with the contents:\n{:} " + "\nWhere `model` is the name of the checkpoint, or explicitly provide the name with " + "--ckpt, not including file extensions".format(self.dir, ckpt_file_contents) + ) input_checkpoint = checkpoint.model_checkpoint_path else: input_checkpoint = os.path.join(self.dir, self.name) - meta_file = input_checkpoint + '.meta' + meta_file = input_checkpoint + ".meta" with tf.Graph().as_default() as graph, tf.compat.v1.Session(graph=graph).as_default() as sess: saver = tf.compat.v1.train.import_meta_graph(meta_file, clear_devices=True) saver.restore(sess, input_checkpoint) @@ -224,8 +229,17 @@ class UseTfTrt(BaseLoader): """ [UNTESTED] Functor that optimizes a TensorFlow model using TF-TRT. """ - def __init__(self, graph, max_workspace_size=None, fp16=None, int8=None, max_batch_size=None, - is_dynamic_op=False, minimum_segment_size=None): + + def __init__( + self, + graph, + max_workspace_size=None, + fp16=None, + int8=None, + max_batch_size=None, + is_dynamic_op=False, + minimum_segment_size=None, + ): """ Optimizes a TensorFlow model using TF-TRT. @@ -237,14 +251,13 @@ class UseTfTrt(BaseLoader): max_batch_size (int): The maximum batch size. """ self._graph = graph - self.max_workspace_size = util.default(max_workspace_size, 1<<24) + self.max_workspace_size = util.default(max_workspace_size, 1 << 24) self.fp16 = util.default(fp16, False) self.int8 = util.default(int8, False) self.max_batch_size = util.default(max_batch_size, 1) self.is_dynamic_op = is_dynamic_op self.minimum_segment_size = util.default(minimum_segment_size, 3) - def call_impl(self): """ Returns: @@ -257,14 +270,27 @@ class UseTfTrt(BaseLoader): precision_mode = "FP16" if self.fp16 else "FP32" precision_mode = "INT8" if self.int8 else precision_mode - G_LOGGER.info("For TF-TRT, using outputs={:}, max_workspace_size_bytes={:}, max_batch_size={:}, " - "minimum_segment_size={:}, is_dynamic_op={:}, precision_mode={:}".format( - output_names, self.max_workspace_size, self.max_batch_size, self.minimum_segment_size, - self.is_dynamic_op, precision_mode)) + G_LOGGER.info( + "For TF-TRT, using outputs={:}, max_workspace_size_bytes={:}, max_batch_size={:}, " + "minimum_segment_size={:}, is_dynamic_op={:}, precision_mode={:}".format( + output_names, + self.max_workspace_size, + self.max_batch_size, + self.minimum_segment_size, + self.is_dynamic_op, + precision_mode, + ) + ) - graphdef = tf_trt.create_inference_graph(graph.as_graph_def(), outputs=output_names, - max_workspace_size_bytes=self.max_workspace_size, max_batch_size=self.max_batch_size, - minimum_segment_size=self.minimum_segment_size, is_dynamic_op=self.is_dynamic_op, precision_mode=precision_mode) + graphdef = tf_trt.create_inference_graph( + graph.as_graph_def(), + outputs=output_names, + max_workspace_size_bytes=self.max_workspace_size, + max_batch_size=self.max_batch_size, + minimum_segment_size=self.minimum_segment_size, + is_dynamic_op=self.is_dynamic_op, + precision_mode=precision_mode, + ) segment_number = 0 for node in graphdef.node: @@ -278,12 +304,13 @@ class UseTfTrt(BaseLoader): return graph, tf_util.get_graph_output_names(graph) -@mod.export_deprecated_alias("ModifyGraph", remove_in="0.30.0") +@mod.export_deprecated_alias("ModifyGraph", remove_in="0.32.0") @mod.export(funcify=True) class ModifyGraphOutputs(BaseLoader): """ Functor that modifies outputs of a TensorFlow graph. """ + def __init__(self, graph, outputs=None): """ Modifies outputs of a TensorFlow graph. @@ -302,7 +329,6 @@ class ModifyGraphOutputs(BaseLoader): self._graph = graph self.outputs = outputs - def call_impl(self): """ Returns: @@ -323,6 +349,7 @@ class SaveGraph(BaseLoader): """ Functor that writes out artifacts from a TensorFlow graph. """ + def __init__(self, graph, path=None, tensorboard_dir=None, engine_dir=None): """ Writes out artifacts from a TensorFlow Graph. @@ -342,7 +369,6 @@ class SaveGraph(BaseLoader): self.tensorboard_dir = tensorboard_dir self.engine_dir = engine_dir - def call_impl(self): """ Returns: @@ -364,8 +390,9 @@ class SaveGraph(BaseLoader): if node.op == "TRTEngineOp": engine = node.attr["serialized_segment"].s if self.engine_dir is not None: - util.save_file(contents=engine, - dest=os.path.join(self.engine_dir, "segment-{:}".format(segment_number))) + util.save_file( + contents=engine, dest=os.path.join(self.engine_dir, "segment-{:}".format(segment_number)) + ) segment_number += 1 return graph, outputs @@ -376,6 +403,7 @@ class CreateConfig(BaseLoader): """ Functor that creates a TensorFlow config. """ + def __init__(self, gpu_memory_fraction=None, allow_growth=None, use_xla=None): """ Creates a TensorFlow config. @@ -391,7 +419,6 @@ class CreateConfig(BaseLoader): self.allow_growth = util.default(allow_growth, False) self.use_xla = util.default(use_xla, False) - def call_impl(self): """ Returns: @@ -399,8 +426,9 @@ class CreateConfig(BaseLoader): """ # Session configuration - gpu_options = tf.compat.v1.GPUOptions(per_process_gpu_memory_fraction=self.gpu_memory_fraction, - allow_growth=self.allow_growth) + gpu_options = tf.compat.v1.GPUOptions( + per_process_gpu_memory_fraction=self.gpu_memory_fraction, allow_growth=self.allow_growth + ) config = tf.compat.v1.ConfigProto(gpu_options=gpu_options) if self.use_xla: config.graph_options.optimizer_options.global_jit_level = tf.OptimizerOptions.ON_1 @@ -413,6 +441,7 @@ class SessionFromGraph(BaseLoader): """ Functor that creates a TensorFlow session that can be used for inference. """ + def __init__(self, graph, config=None): """ Creates a TensorFlow session. @@ -428,7 +457,6 @@ class SessionFromGraph(BaseLoader): self.graph = graph self.config = util.default(config, CreateConfig()) - def call_impl(self): """ Returns: diff --git a/tools/Polygraphy/polygraphy/backend/tf/runner.py b/tools/Polygraphy/polygraphy/backend/tf/runner.py index 8fce23bf..c90eab25 100644 --- a/tools/Polygraphy/polygraphy/backend/tf/runner.py +++ b/tools/Polygraphy/polygraphy/backend/tf/runner.py @@ -25,11 +25,13 @@ from polygraphy.logger import G_LOGGER tf = mod.lazy_import("tensorflow", version="<2.0") + @mod.export() class TfRunner(BaseRunner): """ Runs inference using a TensorFlow session. """ + def __init__(self, sess, timeline_dir=None, name=None): """ Args: @@ -59,27 +61,24 @@ class TfRunner(BaseRunner): self.run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE) self.run_metadata = tf.RunMetadata() - def activate_impl(self): (self.sess, self.output_names), _ = util.invoke_if_callable(self._sess) - @func.constantmethod def get_input_metadata_impl(self): return tf_util.get_input_metadata(self.sess.graph) - def deactivate_impl(self): self.sess.close() del (self.sess, self.output_names) self.num_inferences = 0 - def infer_impl(self, feed_dict): G_LOGGER.extra_verbose("Received feed_dict: {:}".format(feed_dict)) start = time.time() - inference_outputs = self.sess.run(self.output_names, feed_dict=feed_dict, options=self.run_options, - run_metadata=self.run_metadata) + inference_outputs = self.sess.run( + self.output_names, feed_dict=feed_dict, options=self.run_options, run_metadata=self.run_metadata + ) end = time.time() out_dict = OrderedDict() @@ -89,11 +88,14 @@ class TfRunner(BaseRunner): if self.timeline_dir is not None: from tensorflow.python.client import timeline + t1 = timeline.Timeline(self.run_metadata.step_stats) - util.save_file(contents=t1.generate_chrome_trace_format(), - dest=os.path.join(self.timeline_dir, "run-{:}".format(self.num_inferences)), - mode="w") + util.save_file( + contents=t1.generate_chrome_trace_format(), + dest=os.path.join(self.timeline_dir, "run-{:}".format(self.num_inferences)), + mode="w", + ) self.num_inferences += 1 return out_dict diff --git a/tools/Polygraphy/polygraphy/backend/tf/util.py b/tools/Polygraphy/polygraphy/backend/tf/util.py index d99d8bf5..60f481d0 100644 --- a/tools/Polygraphy/polygraphy/backend/tf/util.py +++ b/tools/Polygraphy/polygraphy/backend/tf/util.py @@ -40,11 +40,14 @@ def load_graph(path): graphdef = tf.compat.v1.GraphDef() import google + try: graphdef.ParseFromString(util.load_file(path, description="GraphDef")) except google.protobuf.message.DecodeError: G_LOGGER.backtrace() - G_LOGGER.critical("Could not import TensorFlow GraphDef from: {:}. Is this a valid TensorFlow model?".format(path)) + G_LOGGER.critical( + "Could not import TensorFlow GraphDef from: {:}. Is this a valid TensorFlow model?".format(path) + ) elif isinstance(path, tf.compat.v1.GraphDef): graphdef = path @@ -64,7 +67,7 @@ def map_node_outputs(graphdef): split_input = input_name.split(":") if len(split_input) > 1: split_input.pop(-1) - return ":".join(split_input).replace('^', '') + return ":".join(split_input).replace("^", "") node_outputs = defaultdict(list) for node in graphdef.node: @@ -120,15 +123,17 @@ def get_output_metadata(graph, layerwise=False): "NoOp", "ReadVariableOp", "VarIsInitializedOp", - "Const" - ] + "Const", + ] # Additionally, we sometimes need to exclude entire namespaces e.g. while loops. EXCLUDE_NAMESPACES = ["while", "Assert"] if any([ex_op in node.op for ex_op in EXCLUDE_OPS]) or any([ns in node.name for ns in EXCLUDE_NAMESPACES]): - G_LOGGER.extra_verbose("Excluding {:}, op {:} is not a valid output op or is part of an excluded namespace " - "(Note: excluded namespaces: {:})".format(node.name, node.op, EXCLUDE_NAMESPACES)) + G_LOGGER.extra_verbose( + "Excluding {:}, op {:} is not a valid output op or is part of an excluded namespace " + "(Note: excluded namespaces: {:})".format(node.name, node.op, EXCLUDE_NAMESPACES) + ) return False return True @@ -151,10 +156,14 @@ def get_output_metadata(graph, layerwise=False): except KeyError: G_LOGGER.warning("Could not import: {:}. Skipping.".format(tensor_name)) if len(output_tensors) != len(output_nodes): - G_LOGGER.warning("Excluded {:} ops that don't seem like outputs. Use -vv/--super-verbose, or set " - "logging verbosity to EXTRA_VERBOSE to view them.".format(len(output_nodes) - len(output_tensors))) + G_LOGGER.warning( + "Excluded {:} ops that don't seem like outputs. Use -vv/--super-verbose, or set " + "logging verbosity to EXTRA_VERBOSE to view them.".format(len(output_nodes) - len(output_tensors)) + ) - G_LOGGER.extra_verbose("Found output op types in graph: {:}".format(set([tensor.op.type for tensor in output_tensors]))) + G_LOGGER.extra_verbose( + "Found output op types in graph: {:}".format({tensor.op.type for tensor in output_tensors}) + ) G_LOGGER.verbose("Retrieved TensorFlow output_tensors: {:}".format(output_tensors)) return get_tensor_metadata(output_tensors) @@ -172,8 +181,10 @@ def str_from_graph(graph, mode): graph_str += "---- {:} Graph Outputs ----\n{:}\n\n".format(len(output_metadata), output_metadata) graph_str += "---- {:} Nodes ----\n".format(len(graph.as_graph_def().node)) if mode == "basic": - G_LOGGER.warning("Displaying layer information is unsupported for TensorFlow graphs. " - "Please use --mode=full if you would like to see the raw nodes") + G_LOGGER.warning( + "Displaying layer information is unsupported for TensorFlow graphs. " + "Please use --mode=full if you would like to see the raw nodes" + ) if mode == "full": for node in graph.as_graph_def().node: graph_str += str(node) + "\n" diff --git a/tools/Polygraphy/polygraphy/backend/trt/__init__.py b/tools/Polygraphy/polygraphy/backend/trt/__init__.py index 686f7a1a..f16e1624 100644 --- a/tools/Polygraphy/polygraphy/backend/trt/__init__.py +++ b/tools/Polygraphy/polygraphy/backend/trt/__init__.py @@ -27,6 +27,7 @@ def register_logger_callback(): else: get_trt_logger().min_severity = trt.Logger.VERBOSE - G_LOGGER.register_callback(set_trt_logging_level) # Will be registered when this runner is imported. + G_LOGGER.register_callback(set_trt_logging_level) # Will be registered when this runner is imported. + register_logger_callback() diff --git a/tools/Polygraphy/polygraphy/backend/trt/algorithm_selector.py b/tools/Polygraphy/polygraphy/backend/trt/algorithm_selector.py index 6391f8d8..77e65efe 100644 --- a/tools/Polygraphy/polygraphy/backend/trt/algorithm_selector.py +++ b/tools/Polygraphy/polygraphy/backend/trt/algorithm_selector.py @@ -18,7 +18,8 @@ from polygraphy import func, mod, util from polygraphy.backend.trt import util as trt_util from polygraphy.common.interface import TypedDict from polygraphy.json import Decoder, Encoder, add_json_methods -from polygraphy.logger import G_LOGGER +from polygraphy.logger import G_LOGGER, LogMode +from polygraphy.exception import PolygraphyException trt = mod.lazy_import("tensorrt") @@ -29,12 +30,14 @@ trt = mod.lazy_import("tensorrt") # NOTE: Modifying the structure of the data classes below will break backwards compatiblity + @mod.export() class Algorithm(object): """ Represents a TensorRT algorithm variant, which can be uniquely represented by an implementation ID and tactic ID. """ + @staticmethod def from_trt(context, algorithm): """ @@ -47,16 +50,19 @@ class Algorithm(object): algorithm (trt.IAlgorithm): The algorithm variant provided by TensorRT. """ + def unpack_io_info(io_info): return (io_info.tensor_format, io_info.dtype) implementation = algorithm.algorithm_variant.implementation tactic = algorithm.algorithm_variant.tactic inputs = tuple(unpack_io_info(algorithm.get_algorithm_io_info(i)) for i in range(context.num_inputs)) - outputs = tuple(unpack_io_info(algorithm.get_algorithm_io_info(i)) for i in range(context.num_inputs, context.num_inputs + context.num_outputs)) + outputs = tuple( + unpack_io_info(algorithm.get_algorithm_io_info(i)) + for i in range(context.num_inputs, context.num_inputs + context.num_outputs) + ) return Algorithm(implementation, tactic, inputs, outputs) - def __init__(self, implementation, tactic, inputs, outputs): """ Args: @@ -69,14 +75,19 @@ class Algorithm(object): outputs (List[Tuple[trt.TensorFormat, trt.DataType]]): A list of tuples containg a TensorRT tensor format and data type for each output. """ + def validate_meta(meta): - try: - for (fmt, dtype) in meta: - assert isinstance(fmt, trt.TensorFormat) - assert isinstance(dtype, trt.DataType) - except: - G_LOGGER.critical("Could not validate input/output metadata: {:}. " - "Is it a list of tuples containing (trt.TensorFormat, trt.DataType)?".format(meta)) + for (fmt, dtype) in meta: + if not isinstance(fmt, trt.TensorFormat): + G_LOGGER.critical( + "'format' must be an instance of trt.TensorFormat, but is: {:}.\n" + "Note: Provided input/output metadata was: {:}".format(fmt, meta) + ) + if not isinstance(dtype, trt.DataType): + G_LOGGER.critical( + "'dtype' must be an instance of trt.DataType, but is: {:}.\n" + "Note: Provided input/output metadata was: {:}".format(dtype, meta) + ) return meta self.implementation = implementation @@ -85,21 +96,19 @@ class Algorithm(object): self.inputs = tuple(validate_meta(inputs)) self.outputs = tuple(validate_meta(outputs)) - def __str__(self): def io_str(io): return tuple((str(tensor_format), str(dtype)) for tensor_format, dtype in io) return "(Implementation: {:}, Tactic: {:}) | Inputs: {:} | Outputs: {:}".format( - self.implementation, self.tactic, io_str(self.inputs), io_str(self.outputs)) - + self.implementation, self.tactic, io_str(self.inputs), io_str(self.outputs) + ) def __eq__(self, other): tactic_matches = self.implementation == other.implementation and self.tactic == other.tactic io_matches = self.inputs == other.inputs and self.outputs == other.outputs return tactic_matches and io_matches - def __hash__(self): return hash((self.implementation, self.tactic, self.inputs, self.outputs)) @@ -128,10 +137,12 @@ def decode(dct): decoded.append((util.getattr_nested(trt, fmt), util.getattr_nested(trt, dtype))) return decoded - return Algorithm(implementation=dct["implementation"], - tactic=dct["tactic"], - inputs=decode_algo_io(dct["inputs"]), - outputs=decode_algo_io(dct["outputs"])) + return Algorithm( + implementation=dct["implementation"], + tactic=dct["tactic"], + inputs=decode_algo_io(dct["inputs"]), + outputs=decode_algo_io(dct["outputs"]), + ) @mod.export() @@ -141,6 +152,7 @@ class TacticReplayData(TypedDict(lambda: str, lambda: Algorithm)): Maps layer names to corresponding tactics. More specifically, it is an `OrderedDict[str, Algorithm]` """ + def add(self, name, algorithm): """ Add an entry into the tactic replay data. @@ -158,7 +170,6 @@ class TacticReplayData(TypedDict(lambda: str, lambda: Algorithm)): self.dct[name] = algorithm return self - def __str__(self): return "\n".join(["Layer: {:}\n\tAlgorithm: {:}".format(name, algorithm) for (name, algorithm) in self.items()]) @@ -187,7 +198,6 @@ def get_base_selector_type(): else: IAlgorithmSelector = object - class BaseSelector(IAlgorithmSelector): def __init__(self, data): if not ALGO_SELECTOR_ENABLED: @@ -203,7 +213,6 @@ def get_base_selector_type(): else: self.path = data - def select_algorithms(self, context, choices): return list(range(len(choices))) @@ -223,13 +232,13 @@ def TacticRecorder(record): A path or file-like object or an empty ``TacticReplayData`` instance. Tactics will be recorded and stored here. """ + class TacticRecorderClass(get_base_selector_type()): def __init__(self): super().__init__(record) # The function that constructed this instance self.make_func = TacticRecorder - @G_LOGGER.log_exception def report_algorithms(self, contexts, choices): """ @@ -264,6 +273,7 @@ def TacticReplayer(replay): A path or file-like object containing a JSON-ified ``TacticReplayData`` instance, or a ``TacticReplayData`` instance. """ + class TacticReplayerClass(get_base_selector_type()): def __init__(self): super().__init__(replay) @@ -274,7 +284,6 @@ def TacticReplayer(replay): # The function that constructed this instance self.make_func = TacticReplayer - @G_LOGGER.log_exception @func.constantmethod def select_algorithms(self, context, choices): @@ -300,13 +309,20 @@ def TacticReplayer(replay): """ default_choices = super().select_algorithms(context, choices) - if not self.data: # No replay data, we are in recording mode. + if not self.data: # No replay data, we are in recording mode. return default_choices if context.name not in self.data: - G_LOGGER.warning("Layer: {:} was not found in the tactic replay. Falling back to default tactics. " - "Has the network changed since the tactic replay file was generated? " - "Note: Layers in the tactic replay are: {:}".format(context.name, list(self.data.keys()))) + G_LOGGER.warning( + "Layer: {:} was not found in the tactic replay. Falling back to default tactics.".format( + context.name + ) + ) + G_LOGGER.warning( + "Has the network changed since the tactic replay file was generated?\n" + "Note: Layers in the tactic replay are:\n\t{:}".format("\n\t".join(self.data.keys())), + mode=LogMode.ONCE, + ) return default_choices # Need to find the index of the tactic we want. @@ -314,13 +330,17 @@ def TacticReplayer(replay): tactic_choices = [Algorithm.from_trt(context, algo) for algo in choices] if to_select not in tactic_choices: - G_LOGGER.critical("Tactic was not provided by TensorRT as a choice for this layer. Has the " - "network or builder configuration changed since the replay file was generated? " - "Note:\nTactic was: {:}\nAvailable choices were: {:}".format(to_select, list(map(str, tactic_choices)))) + G_LOGGER.critical( + "Layer: {:} | Tactic in replay was not provided by TensorRT as a choice for this layer.\n" + "Has the network or builder configuration changed since the replay file was generated?\n" + "Note: Tactic in replay was:\n\t{:}\nProvided choices were:\n\t{:}".format( + context.name, to_select, "\n\t".join(map(str, tactic_choices)) + ) + ) return [tactic_choices.index(to_select)] - + @G_LOGGER.log_exception @func.constantmethod def report_algorithms(self, contexts, choices): """ @@ -336,7 +356,11 @@ def TacticReplayer(replay): to_select = self.data[context.name] selected = Algorithm.from_trt(context, choice) if to_select != selected: - G_LOGGER.critical("TensorRT selected a tactic different than the one specified in the tactic replay. " - "Note: Tactic in replay file was: {:}, but TensorRT selected: {:}".format(to_select, selected)) + G_LOGGER.critical( + "Layer: {:} | TensorRT selected a tactic different than the one specified in the tactic replay.\n" + "Note: Tactic in replay was:\n\t{:}, but TensorRT selected:\n\t{:}".format( + context.name, to_select, selected + ) + ) return TacticReplayerClass() diff --git a/tools/Polygraphy/polygraphy/backend/trt/calibrator.py b/tools/Polygraphy/polygraphy/backend/trt/calibrator.py index 5d160a01..da33f613 100644 --- a/tools/Polygraphy/polygraphy/backend/trt/calibrator.py +++ b/tools/Polygraphy/polygraphy/backend/trt/calibrator.py @@ -24,9 +24,9 @@ np = mod.lazy_import("numpy") @mod.export() -def Calibrator(data_loader, cache=None, BaseClass=None, - batch_size=None, quantile=None, regression_cutoff=None, - algo=None): +def Calibrator( + data_loader, cache=None, BaseClass=None, batch_size=None, quantile=None, regression_cutoff=None, algo=None +): """ Supplies calibration data to TensorRT to calibrate the network for INT8 inference. @@ -70,6 +70,7 @@ def Calibrator(data_loader, cache=None, BaseClass=None, """ Calibrator that supplies calibration data to TensorRT to calibrate the network for INT8 inference. """ + def __init__(self): # Must explicitly initialize parent for any trampoline class! Will mysteriously segfault without this. BaseClass.__init__(self) @@ -87,7 +88,6 @@ def Calibrator(data_loader, cache=None, BaseClass=None, # The function that constructed this instance self.make_func = Calibrator - def reset(self, input_metadata=None): """ Reset this calibrator for reuse. @@ -112,24 +112,26 @@ def Calibrator(data_loader, cache=None, BaseClass=None, self.cache_contents = None self.has_cached_scales = False - def get_batch_size(self): return self.batch_size - def get_batch(self, names): if not self.is_active: - G_LOGGER.error("Calibrator must be activated prior to use. Please use a context manager. " - "For example:\nwith calibrator:\n\t# Use calibrator here") + G_LOGGER.error( + "Calibrator must be activated prior to use. Please use a context manager. " + "For example:\nwith calibrator:\n\t# Use calibrator here" + ) return None try: buffers = next(self.data_loader_iter) except StopIteration: if not self.num_batches: - G_LOGGER.error("Calibrator data loader provided no data.\nPossible reasons for this include:\n(1) data loader " - "has no data to provide\n(2) data loader was a generator, and the calibrator is being " - "used multiple times (generators cannot be rewound)") + G_LOGGER.error( + "Calibrator data loader provided no data.\nPossible reasons for this include:\n(1) data loader " + "has no data to provide\n(2) data loader was a generator, and the calibrator is being " + "used multiple times (generators cannot be rewound)" + ) return None else: self.num_batches += 1 @@ -152,14 +154,16 @@ def Calibrator(data_loader, cache=None, BaseClass=None, elif isinstance(buf, int): ptrs.append(buf) else: - G_LOGGER.error("Calibration data loader provided an unrecognized type: {:} for input: {:}.\n" - "Please provide either a NumPy array, Polygraphy DeviceView, or GPU pointer. ".format( - type(buf).__name__, name)) + G_LOGGER.error( + "Calibration data loader provided an unrecognized type: {:} for input: {:}.\n" + "Please provide either a NumPy array, Polygraphy DeviceView, or GPU pointer. ".format( + type(buf).__name__, name + ) + ) return None return ptrs - def read_calibration_cache(self): def load_from_cache(): if self._cache is None or not util.get_file_size(self._cache): @@ -167,11 +171,12 @@ def Calibrator(data_loader, cache=None, BaseClass=None, try: return util.load_file(self._cache, description="calibration cache") - except: - G_LOGGER.warning("Could not read from calibration cache: {:}".format(self._cache)) + except Exception as err: + G_LOGGER.error( + "Could not read from calibration cache: {:}\nNote: Error was: {:}".format(self._cache, err) + ) return None - # Only attempt to read from the cache once. if self.has_cached_scales: return self.cache_contents @@ -180,16 +185,17 @@ def Calibrator(data_loader, cache=None, BaseClass=None, if not self.cache_contents: if self.cache_contents is not None: - G_LOGGER.warning("Calibration cache was provided, but is empty. " - "Will regenerate scales by running calibration.", - mode=LogMode.ONCE) + G_LOGGER.warning( + "Calibration cache was provided, but is empty. " + "Will regenerate scales by running calibration.", + mode=LogMode.ONCE, + ) self.cache_contents = None else: self.has_cached_scales = True return self.cache_contents - def write_calibration_cache(self, cache): self.cache_contents = cache.tobytes() self.has_cached_scales = True @@ -199,47 +205,47 @@ def Calibrator(data_loader, cache=None, BaseClass=None, try: util.save_file(contents=self.cache_contents, dest=self._cache, description="calibration cache") - except: - G_LOGGER.warning("Could not write to calibration cache: {:}".format(self._cache)) - + except Exception as err: + G_LOGGER.error( + "Could not write to calibration cache: {:}.\nNote: Error was: {:}".format(self._cache, err) + ) def __enter__(self): self.is_active = True return self - def __exit__(self, exc_type, exc_value, traceback): self.is_active = False for device_buffer in self.device_buffers.values(): device_buffer.free() - # IInt8LegacyCalibrator methods def get_quantile(self): return util.default(quantile, 0.5) - def get_regression_cutoff(self): return util.default(regression_cutoff, 0.5) - def read_histogram_cache(self, length): pass - def write_histogram_cache(self, ptr, length): pass - # IInt8Calibrator methods def get_algorithm(self): return util.default(algo, trt.CalibrationAlgoType.MINMAX_CALIBRATION) - def __repr__(self): - return util.make_repr("Calibrator", data_loader, cache=cache, BaseClass=BaseClass, - batch_size=batch_size, quantile=quantile, regression_cutoff=regression_cutoff, - algo=algo)[0] - + return util.make_repr( + "Calibrator", + data_loader, + cache=cache, + BaseClass=BaseClass, + batch_size=batch_size, + quantile=quantile, + regression_cutoff=regression_cutoff, + algo=algo, + )[0] return CalibratorClass() diff --git a/tools/Polygraphy/polygraphy/backend/trt/loader.py b/tools/Polygraphy/polygraphy/backend/trt/loader.py index 809d9181..382f3bb6 100644 --- a/tools/Polygraphy/polygraphy/backend/trt/loader.py +++ b/tools/Polygraphy/polygraphy/backend/trt/loader.py @@ -16,14 +16,17 @@ import contextlib import copy import ctypes +import time -from polygraphy import constants, mod, util +from polygraphy import config, constants, mod, util from polygraphy.backend.base import BaseLoader from polygraphy.backend.trt import util as trt_util from polygraphy.backend.trt.profile import Profile from polygraphy.logger import G_LOGGER trt = mod.lazy_import("tensorrt") +gs = mod.lazy_import("onnx_graphsurgeon") +np = mod.lazy_import("numpy") @mod.export(funcify=True) @@ -38,6 +41,7 @@ class LoadPlugins(BaseLoader): load_plugins(plugins=["/path/to/my/plugin.so", "/path/to/my/other_plugin.so"]) """ + def __init__(self, plugins=None, obj=None): """ Loads plugins from the specified paths. @@ -53,7 +57,6 @@ class LoadPlugins(BaseLoader): self.plugins = util.default(plugins, []) self.obj = obj - def call_impl(self, *args, **kwargs): """ Returns: @@ -74,6 +77,7 @@ class CreateNetwork(BaseLoader): """ Functor that creates an empty TensorRT network. """ + def __init__(self, explicit_precision=None, explicit_batch=None): """ Creates an empty TensorRT network. @@ -87,13 +91,12 @@ class CreateNetwork(BaseLoader): self.explicit_precision = util.default(explicit_precision, False) self.explicit_batch = util.default(explicit_batch, True) - def call_impl(self): """ Returns: (trt.Builder, trt.INetworkDefinition): The builder and empty network. """ - with util.FreeOnException([trt.Builder(trt_util.get_trt_logger())]) as (builder, ): + with util.FreeOnException([trt.Builder(trt_util.get_trt_logger())]) as (builder,): network_flags = 0 if self.explicit_batch: network_flags |= 1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH) @@ -114,10 +117,10 @@ class BaseNetworkFromOnnx(BaseLoader): self.explicit_precision = util.default(explicit_precision, False) self.explicit_batch = util.default(explicit_batch, True) - def call_impl(self): - with util.FreeOnException(create_network(explicit_precision=self.explicit_precision, - explicit_batch=self.explicit_batch)) as (builder, network): + with util.FreeOnException( + create_network(explicit_precision=self.explicit_precision, explicit_batch=self.explicit_batch) + ) as (builder, network): parser = trt.OnnxParser(network, trt_util.get_trt_logger()) return builder, network, parser @@ -127,6 +130,7 @@ class NetworkFromOnnxBytes(BaseNetworkFromOnnx): """ Functor that parses an ONNX model to create a trt.INetworkDefinition. """ + def __init__(self, model_bytes, explicit_precision=None): """ Parses an ONNX model. @@ -138,7 +142,6 @@ class NetworkFromOnnxBytes(BaseNetworkFromOnnx): super().__init__(explicit_precision) self._model_bytes = model_bytes - def call_impl(self): """ Returns: @@ -152,13 +155,13 @@ class NetworkFromOnnxBytes(BaseNetworkFromOnnx): return builder, network, parser - @mod.export(funcify=True) class NetworkFromOnnxPath(BaseNetworkFromOnnx): """ Functor that parses an ONNX model to create a trt.INetworkDefinition. This loader supports models with weights stored in an external location. """ + def __init__(self, path, explicit_precision=None): """ Parses an ONNX model from a file. @@ -169,7 +172,6 @@ class NetworkFromOnnxPath(BaseNetworkFromOnnx): super().__init__(explicit_precision) self.path = path - def call_impl(self): """ Returns: @@ -187,15 +189,17 @@ class NetworkFromOnnxPath(BaseNetworkFromOnnx): return builder, network, parser else: from polygraphy.backend.common import bytes_from_path + return network_from_onnx_bytes(bytes_from_path(path), self.explicit_precision) -@mod.export_deprecated_alias("ModifyNetwork", remove_in="0.30.0") +@mod.export_deprecated_alias("ModifyNetwork", remove_in="0.32.0") @mod.export(funcify=True) class ModifyNetworkOutputs(BaseLoader): """ Functor that modifies outputs in a TensorRT ``INetworkDefinition``. """ + def __init__(self, network, outputs=None, exclude_outputs=None): """ Modifies outputs in a TensorRT ``INetworkDefinition``. @@ -219,7 +223,6 @@ class ModifyNetworkOutputs(BaseLoader): self.outputs = outputs self.exclude_outputs = exclude_outputs - def call_impl(self): """ Returns: @@ -250,9 +253,22 @@ class CreateConfig(BaseLoader): """ Functor that creates a TensorRT IBuilderConfig. """ - def __init__(self, max_workspace_size=None, tf32=None, fp16=None, int8=None, profiles=None, - calibrator=None, strict_types=None, load_timing_cache=None, algorithm_selector=None, - sparse_weights=None, tactic_sources=None): + + def __init__( + self, + max_workspace_size=None, + tf32=None, + fp16=None, + int8=None, + profiles=None, + calibrator=None, + strict_types=None, + load_timing_cache=None, + algorithm_selector=None, + sparse_weights=None, + tactic_sources=None, + restricted=None, + ): """ Creates a TensorRT IBuilderConfig that can be used by EngineFromNetwork. @@ -273,7 +289,7 @@ class CreateConfig(BaseLoader): A list of optimization profiles to add to the configuration. Only needed for networks with dynamic input shapes. If this is omitted for a network with dynamic shapes, a default profile is created, where dynamic dimensions are - replaced with Polygraphy's DEFAULT_SHAPE_VALUE (defined in util/constants.py). + replaced with Polygraphy's DEFAULT_SHAPE_VALUE (defined in constants.py). A partially populated profile will be automatically filled using values from ``Profile.fill_defaults()`` See ``Profile`` for details. calibrator (trt.IInt8Calibrator): @@ -300,6 +316,10 @@ class CreateConfig(BaseLoader): TensorRT is allowed to load tactics from. Use an empty list to disable all tactic sources. Defaults to TensorRT's default tactic sources. + restricted (bool): + Whether to enable safety scope checking in the builder. This will check if the network + and builder configuration are compatible with safety scope. + Defaults to False. """ self.max_workspace_size = util.default(max_workspace_size, 1 << 24) self.tf32 = util.default(tf32, False) @@ -308,15 +328,17 @@ class CreateConfig(BaseLoader): self.profiles = util.default(profiles, [Profile()]) self.calibrator = calibrator self.strict_types = util.default(strict_types, False) + self.restricted = util.default(restricted, False) self.timing_cache_path = load_timing_cache self.algorithm_selector = algorithm_selector self.sparse_weights = util.default(sparse_weights, False) self.tactic_sources = tactic_sources if self.calibrator is not None and not self.int8: - G_LOGGER.warning("A calibrator was provided to `CreateConfig`, but int8 mode was not enabled. " - "Did you mean to set `int8=True` to enable building with int8 precision?") - + G_LOGGER.warning( + "A calibrator was provided to `CreateConfig`, but int8 mode was not enabled. " + "Did you mean to set `int8=True` to enable building with int8 precision?" + ) def call_impl(self, builder, network): """ @@ -330,18 +352,17 @@ class CreateConfig(BaseLoader): Returns: trt.IBuilderConfig: The TensorRT builder configuration. """ - with util.FreeOnException([builder.create_builder_config()]) as (config, ): + with util.FreeOnException([builder.create_builder_config()]) as (config,): + def try_run(func, name): try: return func() except AttributeError: trt_util.fail_unavailable("{:} in CreateConfig".format(name)) - def try_set_flag(flag_name): return try_run(lambda: config.set_flag(getattr(trt.BuilderFlag, flag_name)), flag_name.lower()) - with G_LOGGER.indent(): G_LOGGER.verbose("Setting TensorRT Optimization Profiles") profiles = copy.deepcopy(self.profiles) @@ -356,9 +377,12 @@ class CreateConfig(BaseLoader): if self.strict_types: try_set_flag("STRICT_TYPES") + if self.restricted: + try_set_flag("SAFETY_SCOPE") + if self.tf32: try_set_flag("TF32") - else: # TF32 is on by default + else: # TF32 is on by default with contextlib.suppress(AttributeError): config.clear_flag(trt.BuilderFlag.TF32) @@ -370,7 +394,7 @@ class CreateConfig(BaseLoader): if not network.has_explicit_precision: if self.calibrator is not None: input_metadata = trt_util.get_input_metadata_from_profile(trt_profile, network) - with contextlib.suppress(AttributeError): # Polygraphy calibrator has a reset method + with contextlib.suppress(AttributeError): # Polygraphy calibrator has a reset method self.calibrator.reset(input_metadata) config.int8_calibrator = self.calibrator try: @@ -378,8 +402,10 @@ class CreateConfig(BaseLoader): except: G_LOGGER.extra_verbose("Cannot set calibration profile on TensorRT 7.0 and older.") else: - G_LOGGER.warning("Network does not have explicit precision and no calibrator was provided. Please ensure " - "that tensors in the network have dynamic ranges set, or provide a calibrator in order to use int8 mode.") + G_LOGGER.warning( + "Network does not have explicit precision and no calibrator was provided. Please ensure " + "that tensors in the network have dynamic ranges set, or provide a calibrator in order to use int8 mode." + ) if self.sparse_weights: try_set_flag("SPARSE_WEIGHTS") @@ -387,7 +413,7 @@ class CreateConfig(BaseLoader): if self.tactic_sources is not None: tactic_sources_flag = 0 for source in self.tactic_sources: - tactic_sources_flag |= (1 << int(source)) + tactic_sources_flag |= 1 << int(source) try_run(lambda: config.set_tactic_sources(tactic_sources_flag), name="tactic_sources") try: @@ -405,8 +431,10 @@ class CreateConfig(BaseLoader): config.set_timing_cache(cache, ignore_mismatch=False) if self.algorithm_selector is not None: + def set_algo_selector(): config.algorithm_selector = self.algorithm_selector + try_run(set_algo_selector, "algorithm_selector") return config @@ -417,6 +445,7 @@ class EngineBytesFromNetwork(BaseLoader): """ Functor that uses a TensorRT ``INetworkDefinition`` to build a serialized engine. """ + def __init__(self, network, config=None, save_timing_cache=None): """ Builds and serializes TensorRT engine. @@ -445,7 +474,6 @@ class EngineBytesFromNetwork(BaseLoader): self._config = util.default(config, CreateConfig()) self.timing_cache_path = save_timing_cache - def call_impl(self): """ Returns: @@ -456,8 +484,10 @@ class EngineBytesFromNetwork(BaseLoader): builder, network, parser = util.unpack_args(ret, num=3) if builder is None or network is None: - G_LOGGER.critical("Expected to recevie a (builder, network) tuple for the `network` parameter, " - "but received: ({:}, {:})".format(builder, network)) + G_LOGGER.critical( + "Expected to recevie a (builder, network) tuple for the `network` parameter, " + "but received: ({:}, {:})".format(builder, network) + ) with contextlib.ExitStack() as stack: if owns_network: @@ -467,28 +497,35 @@ class EngineBytesFromNetwork(BaseLoader): stack.enter_context(parser) else: provided = "Builder and Network" if parser is None else "Builder, Network, and Parser" - G_LOGGER.verbose("{:} were provided directly instead of via a Callable. This loader will not assume ownership. " - "Please ensure that they are freed.".format(provided)) + G_LOGGER.verbose( + "{:} were provided directly instead of via a Callable. This loader will not assume ownership. " + "Please ensure that they are freed.".format(provided) + ) config, owns_config = util.invoke_if_callable(self._config, builder, network) if owns_config: stack.enter_context(config) else: - G_LOGGER.verbose("Builder configuration was provided directly instead of via a Callable. This loader will not assume " - "ownership. Please ensure it is freed.") + G_LOGGER.verbose( + "Builder configuration was provided directly instead of via a Callable. This loader will not assume " + "ownership. Please ensure it is freed." + ) try: - config.int8_calibrator.__enter__ # Polygraphy calibrator frees device buffers on exit. + config.int8_calibrator.__enter__ # Polygraphy calibrator frees device buffers on exit. except AttributeError: pass else: stack.enter_context(config.int8_calibrator) network_log_mode = "full" if G_LOGGER.severity <= G_LOGGER.ULTRA_VERBOSE else "attrs" - G_LOGGER.super_verbose(lambda: ("Displaying TensorRT Network:\n" + trt_util.str_from_network(network, mode=network_log_mode))) + G_LOGGER.super_verbose( + lambda: ("Displaying TensorRT Network:\n" + trt_util.str_from_network(network, mode=network_log_mode)) + ) G_LOGGER.start("Building engine with configuration:\n{:}".format(trt_util.str_from_config(config))) + start_time = time.time() try: engine_bytes = builder.build_serialized_network(network, config) except AttributeError: @@ -497,9 +534,12 @@ class EngineBytesFromNetwork(BaseLoader): G_LOGGER.critical("Invalid Engine. Please ensure the engine was built correctly") stack.enter_context(engine) engine_bytes = engine.serialize() + end_time = time.time() if not engine_bytes: - G_LOGGER.critical("Invalid Engine. Please ensure the engine_bytes was built correctly") + G_LOGGER.critical("Invalid Engine. Please ensure the engine was built correctly") + + G_LOGGER.finish("Finished engine building in {:.3f} seconds".format(end_time - start_time)) try: timing_cache = config.get_timing_cache() @@ -520,6 +560,7 @@ class EngineFromNetwork(EngineBytesFromNetwork): Similar to EngineBytesFromNetwork, but returns an ICudaEngine instance instead of a serialized engine. """ + def call_impl(self): """ Returns: @@ -535,6 +576,7 @@ class EngineFromBytes(BaseLoader): """ Functor that deserializes an engine from a buffer. """ + def __init__(self, serialized_engine): """ Deserializes an engine from a buffer. @@ -545,7 +587,6 @@ class EngineFromBytes(BaseLoader): """ self._serialized_engine = serialized_engine - def call_impl(self): """ Returns: @@ -557,7 +598,7 @@ class EngineFromBytes(BaseLoader): with contextlib.ExitStack() as stack, trt.Runtime(trt_util.get_trt_logger()) as runtime: if owns_buffer: try: - buffer.__enter__ # IHostMemory is freed only in __exit__ + buffer.__enter__ # IHostMemory is freed only in __exit__ except AttributeError: pass else: @@ -574,6 +615,7 @@ class BytesFromEngine(BaseLoader): """ Functor that serializes an engine. """ + def __init__(self, engine): """ Serializes an engine. @@ -584,7 +626,6 @@ class BytesFromEngine(BaseLoader): """ self._engine = engine - def call_impl(self): """ Returns: @@ -605,6 +646,7 @@ class SaveEngine(BaseLoader): """ Functor that saves an engine to the provided path. """ + def __init__(self, engine, path): """ Saves an engine to the provided path. @@ -619,7 +661,6 @@ class SaveEngine(BaseLoader): self._engine = engine self.path = path - def call_impl(self): """ Returns: @@ -633,3 +674,113 @@ class SaveEngine(BaseLoader): util.save_file(contents=bytes_from_engine(engine), dest=self.path, description="engine") return engine + + +@mod.export(funcify=True) +class OnnxLikeFromNetwork(BaseLoader): + """ + Functor that creates an ONNX-like, but **not** valid ONNX, model based on a TensorRT network. + """ + + def __init__(self, network) -> None: + """ + [HIGHLY EXPERIMENTAL] Creates an ONNX-like, but **not** valid ONNX, model from a TensorRT network. + This uses the ONNX format, but generates nodes that are **not** valid ONNX operators. + Hence, the resulting model is **not** valid ONNX. + This should be used **only** for visualization or debugging purposes. + + The resulting model does **not** include enough information to faithfully reconstruct the TensorRT network. + + Args: + network (Callable() -> trt.Builder, trt.INetworkDefinition): + A callable capable of returning a TensorRT Builder and INetworkDefinition. The callable may + have at most 3 return values if another object needs to be kept alive for the duration of the network, + e.g., in the case of a parser. The first and second return values must always be the builder and network respectively. + If instead of a loader, the network, builder, and optional parser arguments are provided directly, + then OnnxLikeFromNetwork will *not* deallocate them. + """ + self._network = network + + def call_impl(self): + """ + Returns: + onnx.ModelProto: The ONNX-like, but **not** valid ONNX, model. + """ + ret, owns_network = util.invoke_if_callable(self._network) + builder, network, parser = util.unpack_args(ret, num=3) + + if builder is None or network is None: + G_LOGGER.critical( + "Expected to recevie a (builder, network) tuple for the `network` parameter, " + "but received: ({:}, {:})".format(builder, network) + ) + + with contextlib.ExitStack() as stack: + if owns_network: + stack.enter_context(builder) + stack.enter_context(network) + if parser is not None: + stack.enter_context(parser) + + tensor_map = {} + + def tensors_from_meta(meta): + nonlocal tensor_map + tensors = [] + for name, (dtype, shape) in meta.items(): + if name not in tensor_map: + tensor_map[name] = gs.Variable(name=name, dtype=dtype, shape=shape) + tensors.append(tensor_map[name]) + return tensors + + nodes = [] + graph_inputs = tensors_from_meta(trt_util.get_network_input_metadata(network)) + graph_outputs = tensors_from_meta(trt_util.get_network_output_metadata(network)) + + LAYER_TYPE_CLASS_MAPPING = trt_util.get_layer_class_mapping() + + for layer in network: + op_name = layer.type.name + if layer.type in LAYER_TYPE_CLASS_MAPPING: + layer.__class__ = LAYER_TYPE_CLASS_MAPPING[layer.type] + + node_inputs = tensors_from_meta(trt_util.get_layer_input_metadata(layer)) + node_outputs = tensors_from_meta(trt_util.get_layer_output_metadata(layer)) + attrs = {} + attr_names = trt_util.get_layer_attribute_names(layer) + for name in attr_names: + with G_LOGGER.verbosity(): + attr = getattr(layer, name) + + if util.is_sequence(attr) or any(isinstance(attr, cls) for cls in [trt.Dims, trt.Permutation]): + try: + attr = list(attr) + except ValueError: # Invalid dims + attr = [] + + if hasattr(attr, "__entries"): # TensorRT Enums + attr = attr.name + + if isinstance(attr, trt.ILoop): + attr = attr.name + + VALID_TYPES = [np.ndarray, list, int, str, bool, float] + if not any(isinstance(attr, cls) for cls in VALID_TYPES): + G_LOGGER.internal_error( + "Unknown type: {:} for layer attribute: {:}.\n" + "Note: Layer was: {:}".format(type(attr), attr, layer) + ) + try: + attr = str(attr) + except: + attr = "" + + attrs[name] = attr + + nodes.append( + gs.Node(name=layer.name, op=op_name, attrs=attrs, inputs=node_inputs, outputs=node_outputs) + ) + + graph = gs.Graph(name=network.name, inputs=graph_inputs, outputs=graph_outputs, nodes=nodes) + + return gs.export_onnx(graph) diff --git a/tools/Polygraphy/polygraphy/backend/trt/profile.py b/tools/Polygraphy/polygraphy/backend/trt/profile.py index 65f99ac9..1118071c 100644 --- a/tools/Polygraphy/polygraphy/backend/trt/profile.py +++ b/tools/Polygraphy/polygraphy/backend/trt/profile.py @@ -24,6 +24,7 @@ class ShapeTuple(object): """ Represents a set of shapes for a single binding in a profile. """ + def __init__(self, min, opt, max): """ Args: @@ -35,14 +36,15 @@ class ShapeTuple(object): self.opt = opt self.max = max - def __str__(self): return "(min={:}, opt={:}, max={:})".format(self.min, self.opt, self.max) - def __repr__(self): return type(self).__name__ + self.__str__() + def __iter__(self): + yield from [self.min, self.opt, self.max] + @mod.export() class Profile(TypedDict(lambda: str, lambda: ShapeTuple)): @@ -53,6 +55,7 @@ class Profile(TypedDict(lambda: str, lambda: ShapeTuple)): More specifically, this is a OrderedDict[str, ShapeTuple] which maps binding names to a set of min/opt/max shapes. """ + def add(self, name, min, opt, max): """ A convenience function to add shapes for a single binding. @@ -71,7 +74,6 @@ class Profile(TypedDict(lambda: str, lambda: ShapeTuple)): self[name] = ShapeTuple(min, opt, max) return self - def __getitem__(self, key): """ Retrieves the shapes registered for a given input name. @@ -85,7 +87,6 @@ class Profile(TypedDict(lambda: str, lambda: ShapeTuple)): G_LOGGER.critical("Binding: {:} does not have shapes set in this profile".format(key)) return super().__getitem__(key) - def fill_defaults(self, network, default_shape_value=None): """ Fill this profile with sane default values for any bindings whose @@ -109,27 +110,40 @@ class Profile(TypedDict(lambda: str, lambda: ShapeTuple)): if inp.name in self: continue - with G_LOGGER.verbosity(G_LOGGER.CRITICAL): # WAR for spam from TRT + with G_LOGGER.verbosity(G_LOGGER.CRITICAL): # WAR for spam from TRT is_shape_tensor = inp.is_shape_tensor if is_shape_tensor: rank = inp.shape[0] - shape = (default_shape_value, ) * rank - G_LOGGER.warning("{:} | No values provided; Will use input values: {:} for min/opt/max in profile.\n".format( - trt_util.str_from_tensor(inp, is_shape_tensor), shape, rank), mode=LogMode.ONCE) - G_LOGGER.warning("This will cause the shape-tensor to have static values. If this is incorrect, please " - "set the range of values for this input shape-tensor.", mode=LogMode.ONCE) + shape = (default_shape_value,) * rank + G_LOGGER.warning( + "{:} | No values provided; Will use input values: {:} for min/opt/max in profile.\n".format( + trt_util.str_from_tensor(inp, is_shape_tensor), shape, rank + ), + mode=LogMode.ONCE, + ) + G_LOGGER.warning( + "This will cause the shape-tensor to have static values. If this is incorrect, please " + "set the range of values for this input shape-tensor.", + mode=LogMode.ONCE, + ) else: shape = util.override_dynamic_shape(inp.shape, default_shape_value) if shape != inp.shape: - G_LOGGER.warning("{:} | No shapes provided; Will use shape: {:} for min/opt/max in profile.\n".format( - trt_util.str_from_tensor(inp, is_shape_tensor), shape), mode=LogMode.ONCE) - G_LOGGER.warning("This will cause the tensor to have a static shape. If this is incorrect, please " - "set the range of shapes for this input tensor.", mode=LogMode.ONCE) + G_LOGGER.warning( + "{:} | No shapes provided; Will use shape: {:} for min/opt/max in profile.\n".format( + trt_util.str_from_tensor(inp, is_shape_tensor), shape + ), + mode=LogMode.ONCE, + ) + G_LOGGER.warning( + "This will cause the tensor to have a static shape. If this is incorrect, please " + "set the range of shapes for this input tensor.", + mode=LogMode.ONCE, + ) self.add(inp.name, shape, shape, shape) return self - def to_trt(self, builder, network): """ Creates a TensorRT IOptimizationProfile based on the values set in this Profile. @@ -152,28 +166,53 @@ class Profile(TypedDict(lambda: str, lambda: ShapeTuple)): unused_keys.remove(inp.name) available_inputs.add(inp.name) - with G_LOGGER.verbosity(): # WAR for spam from TRT + with G_LOGGER.verbosity(): # WAR for spam from TRT is_shape_tensor = inp.is_shape_tensor if is_shape_tensor: if inp.name in self: shapes = self[inp.name] trt_profile.set_shape_input(inp.name, shapes.min, shapes.opt, shapes.max) - G_LOGGER.verbose("{:} | Setting input shape-tensor value range to: {:}".format( - trt_util.str_from_tensor(inp, is_shape_tensor), shapes)) + G_LOGGER.verbose( + "{:} | Setting input shape-tensor value range to: {:}".format( + trt_util.str_from_tensor(inp, is_shape_tensor), shapes + ) + ) else: - G_LOGGER.warning("{:} | No values provided. " - "Assuming this is not a dynamic shape-tensor.".format( - trt_util.str_from_tensor(inp, is_shape_tensor)), mode=LogMode.ONCE) + G_LOGGER.warning( + "{:} | No values provided. " + "Assuming this is not a dynamic shape-tensor.".format( + trt_util.str_from_tensor(inp, is_shape_tensor) + ), + mode=LogMode.ONCE, + ) else: shapes = self[inp.name] trt_profile.set_shape(inp.name, shapes.min, shapes.opt, shapes.max) - G_LOGGER.verbose("{:} | Setting input tensor shapes to: {:}".format( - trt_util.str_from_tensor(inp, is_shape_tensor), shapes)) + G_LOGGER.verbose( + "{:} | Setting input tensor shapes to: {:}".format( + trt_util.str_from_tensor(inp, is_shape_tensor), shapes + ) + ) if unused_keys: - G_LOGGER.error("Invalid inputs were provided to the optimization profile: {:}\n" - "Note: Inputs available in the TensorRT network are: {:}".format( - unused_keys, available_inputs)) + G_LOGGER.error( + "Invalid inputs were provided to the optimization profile: {:}\n" + "Note: Inputs available in the TensorRT network are: {:}".format(unused_keys, available_inputs) + ) return trt_util.check_profile(trt_profile) + + def __repr__(self): + ret = "Profile()" + for name, (min, opt, max) in self.items(): + ret += ".add({:}, min={:}, opt={:}, max={:})".format(name, min, opt, max) + return ret + + def __str__(self): + elems = [] + for name, (min, opt, max) in self.items(): + elems.append("{:} [min={:}, opt={:}, max={:}]".format(name, min, opt, max)) + + sep = ",\n " + return "{" + sep.join(elems) + "}" diff --git a/tools/Polygraphy/polygraphy/backend/trt/runner.py b/tools/Polygraphy/polygraphy/backend/trt/runner.py index ab3c38c6..c1df5fda 100644 --- a/tools/Polygraphy/polygraphy/backend/trt/runner.py +++ b/tools/Polygraphy/polygraphy/backend/trt/runner.py @@ -35,15 +35,18 @@ class TrtRunner(BaseRunner): Note that runners are not designed for production deployment and should generally be used only for prototyping, testing, and debugging. """ + def __init__(self, engine, name=None): """ Args: engine (Callable() -> Union[trt.ICudaEngine, trt.IExecutionContext]): A callable that can supply either a TensorRT engine or execution context. If an engine is provided, the runner will create a context automatically. - Otherwise, it will use the provided context. - If instead of a callable, the object is provided directly, then the runner - will *not* take ownership of it, and therefore will not destroy it. + This callable is invoked whenever the runner is activated. + + Alternatively, the engine or context may be supplied directly instead of + through a callable, in which case the runner will *not* take ownership of it, + and therefore will not destroy it. name (str): @@ -53,13 +56,11 @@ class TrtRunner(BaseRunner): super().__init__(name=name, prefix="trt-runner") self._engine_or_context = engine - @func.constantmethod def get_input_metadata_impl(self): - bindings_per_profile = trt_util.get_bindings_per_profile(self.context.engine) + start_binding, end_binding = trt_util.get_active_profile_bindings(self.context) # This function always uses binding names of the 0th profile. - return trt_util.get_input_metadata_from_engine(self.context.engine, start_binding=0, end_binding=bindings_per_profile) - + return trt_util.get_input_metadata_from_engine(self.context.engine, start_binding, end_binding) def activate_impl(self): def make_buffers(engine): @@ -79,7 +80,6 @@ class TrtRunner(BaseRunner): G_LOGGER.extra_verbose("Created device buffers: {:}".format(device_buffers)) return device_buffers, host_output_buffers - engine_or_context, owning = util.invoke_if_callable(self._engine_or_context) if isinstance(engine_or_context, trt.ICudaEngine): @@ -95,25 +95,29 @@ class TrtRunner(BaseRunner): self.context = engine_or_context self.owns_context = owning else: - G_LOGGER.critical("Invalid Engine or Context. Please ensure the engine was built correctly. See error log for details.") + G_LOGGER.critical( + "Invalid Engine or Context. Please ensure the engine was built correctly. See error log for details." + ) if not owning: - G_LOGGER.verbose("Object was provided directly instead of via a Callable. This runner will not assume ownership. " - "Please ensure it is freed.") - + G_LOGGER.verbose( + "Object was provided directly instead of via a Callable. This runner will not assume ownership. " + "Please ensure it is freed." + ) self.device_buffers, self.host_output_buffers = make_buffers(self.context.engine) self.stream = cuda.Stream() - def set_profile(self, index): """ Sets the active optimization profile for this runner. + The runner must already be active (see ``__enter__()`` or ``activate()``). + This only applies if your engine was built with multiple optimization profiles. - The profile will be set asynchronously using this runner's CUDA - stream (``runner.stream``). + In TensorRT 8.0 and newer, the profile will be set asynchronously + using this runner's CUDA stream (``runner.stream``). By default, the runner uses the first profile (profile 0). @@ -121,6 +125,9 @@ class TrtRunner(BaseRunner): index (int): The index of the optimization profile to use. """ + if not self.is_active: + G_LOGGER.critical("{:35} | Must be activated prior to calling set_profile()".format(self.name)) + try: self.context.set_optimization_profile_async except AttributeError: @@ -128,7 +135,6 @@ class TrtRunner(BaseRunner): else: self.context.set_optimization_profile_async(index, self.stream.ptr) - def _set_shapes_from_feed_dict(self, feed_dict): """ Sets context shapes according to the provided feed_dict. @@ -143,6 +149,7 @@ class TrtRunner(BaseRunner): Returns: Tuple[int, int]: The start and end binding indices of the modified bindings. """ + def is_dynamic_shape_input(binding): try: self.context.engine.get_profile_shape_input(0, binding) @@ -155,11 +162,13 @@ class TrtRunner(BaseRunner): binding = start_binding + self.context.engine[name] # Only set shapes if required. # get_shape/get_binding_shape will return what a shape input/data input is currently set to. - if is_dynamic_shape_input(binding): # For input shape tensors + if is_dynamic_shape_input(binding): # For input shape tensors if isinstance(inp, cuda.DeviceView): - G_LOGGER.critical("A DeviceView was provided for input: {:}, but since this is a " - "shape tensor, it must reside in host memory. " - "Please use a NumPy array instead. ".format(name)) + G_LOGGER.critical( + "A DeviceView was provided for input: {:}, but since this is a " + "shape tensor, it must reside in host memory. " + "Please use a NumPy array instead. ".format(name) + ) if tuple(self.context.get_shape(binding)) != tuple(inp): G_LOGGER.verbose("Setting shape binding: {:} (index: {:}) to: {:}".format(name, binding, inp)) @@ -172,22 +181,25 @@ class TrtRunner(BaseRunner): self.context.set_binding_shape(binding, shape) if not self.context.all_binding_shapes_specified: - G_LOGGER.critical("Some input shapes were not specified.\n" - "Note: Network inputs are: {:}".format(self.get_input_metadata())) + G_LOGGER.critical( + "Some input shapes were not specified.\n" + "Note: Network inputs are: {:}".format(self.get_input_metadata()) + ) if not self.context.all_shape_inputs_specified: - G_LOGGER.critical("Some shape inputs were not specified.\n" - "Note: Network inputs are: {:}".format(self.get_input_metadata())) + G_LOGGER.critical( + "Some shape inputs were not specified.\n" + "Note: Network inputs are: {:}".format(self.get_input_metadata()) + ) return start_binding, end_binding - def infer_impl(self, feed_dict): start_binding, end_binding = self._set_shapes_from_feed_dict(feed_dict) # Resize output device buffers - host buffers will be automatically resized by copy_to for binding in range(start_binding, end_binding): if not self.context.engine.binding_is_input(binding): - name = self.context.engine[binding - start_binding] # Use profile 0 binding names for all buffers. + name = self.context.engine[binding - start_binding] # Use profile 0 binding names for all buffers. shape = tuple(self.context.get_binding_shape(binding)) self.device_buffers[name].resize(shape) @@ -201,9 +213,10 @@ class TrtRunner(BaseRunner): elif isinstance(buffer, np.ndarray): dev_bufs[name].copy_from(buffer, self.stream) else: - G_LOGGER.critical("Unrecognized type in feed_dict: {:} for input: {:}.\n" - "Please provide either a NumPy array or Polygraphy DeviceView. ".format( - type(buffer).__name__, name)) + G_LOGGER.critical( + "Unrecognized type in feed_dict: {:} for input: {:}.\n" + "Please provide either a NumPy array or Polygraphy DeviceView. ".format(type(buffer).__name__, name) + ) # Need to offset bindings in case the active profile is not 0. bindings = [0] * start_binding + [buf.ptr for buf in dev_bufs.values()] @@ -221,7 +234,6 @@ class TrtRunner(BaseRunner): return self.host_output_buffers - def deactivate_impl(self): with contextlib.ExitStack() as stack: if self.owns_engine: @@ -232,9 +244,15 @@ class TrtRunner(BaseRunner): [buf.free() for buf in self.device_buffers.values()] self.stream.free() - del (self.engine, self.owns_engine, self.context, self.owns_context, - self.device_buffers, self.host_output_buffers, self.stream) - + del ( + self.engine, + self.owns_engine, + self.context, + self.owns_context, + self.device_buffers, + self.host_output_buffers, + self.stream, + ) # Note: This can be removed once TRT 6 support is dropped. def infer(self, feed_dict, check_inputs=None): diff --git a/tools/Polygraphy/polygraphy/backend/trt/util.py b/tools/Polygraphy/polygraphy/backend/trt/util.py index 6b431217..388eac81 100644 --- a/tools/Polygraphy/polygraphy/backend/trt/util.py +++ b/tools/Polygraphy/polygraphy/backend/trt/util.py @@ -24,6 +24,8 @@ np = mod.lazy_import("numpy") TRT_LOGGER = None + + @mod.export() def get_trt_logger(): """ @@ -50,8 +52,7 @@ def check_onnx_parser_errors(parser, success): G_LOGGER.critical("Could not parse ONNX correctly") if not success: - G_LOGGER.critical("Failed to parse ONNX model. " - "Does the model file exist and contain a valid ONNX model?") + G_LOGGER.critical("Failed to parse ONNX model. Does the model file exist and contain a valid ONNX model?") def get_layer_class_mapping(): @@ -63,11 +64,12 @@ def get_layer_class_mapping(): layer_cls = getattr(trt, layer_cls) except AttributeError: if config.INTERNAL_CORRECTNESS_CHECKS: - G_LOGGER.warning("Could not find one or more of layer type: {:} or layer class: {:}".format(layer_type, layer_cls)) + G_LOGGER.warning( + "Could not find one or more of layer type: {:} or layer class: {:}".format(layer_type, layer_cls) + ) else: layer_class_mapping[layer_type] = layer_cls - try_add("CONVOLUTION", "IConvolutionLayer") try_add("FULLY_CONNECTED", "IFullyConnectedLayer") try_add("ACTIVATION", "IActivationLayer") @@ -112,7 +114,7 @@ def np_dtype_from_trt(trt_dtype): return np.dtype(trt.nptype(trt_dtype)) -def get_input_metadata(network): +def get_network_input_metadata(network): inputs = TensorMetadata() for i in range(network.num_inputs): tensor = network.get_input(i) @@ -120,7 +122,7 @@ def get_input_metadata(network): return inputs -def get_output_metadata(network): +def get_network_output_metadata(network): outputs = TensorMetadata() for i in range(network.num_outputs): tensor = network.get_output(i) @@ -128,29 +130,58 @@ def get_output_metadata(network): return outputs +def get_layer_input_metadata(layer): + meta = TensorMetadata() + for i in range(layer.num_inputs): + inp = layer.get_input(i) + if inp: + meta.add(inp.name, np_dtype_from_trt(inp.dtype), inp.shape) + return meta + + +def get_layer_output_metadata(layer): + meta = TensorMetadata() + for i in range(layer.num_outputs): + outp = layer.get_output(i) + if outp: + meta.add(outp.name, np_dtype_from_trt(outp.dtype), outp.shape) + return meta + + def str_from_layer(layer, index): - def get_layer_input_metadata(layer): - meta = TensorMetadata() - for i in range(layer.num_inputs): - inp = layer.get_input(i) - if inp: - meta.add(inp.name, np_dtype_from_trt(inp.dtype), inp.shape) - return meta - - def get_layer_output_metadata(layer): - meta = TensorMetadata() - for i in range(layer.num_outputs): - outp = layer.get_output(i) - if outp: - meta.add(outp.name, np_dtype_from_trt(outp.dtype), outp.shape) - return meta - input_info = get_layer_input_metadata(layer) output_info = get_layer_output_metadata(layer) - return util.str_from_layer("Layer", index, layer.name, layer.type, input_info, output_info) +def get_layer_attribute_names(layer): + def is_special_attribute(attr): + return attr.startswith("__") and attr.endswith("__") + + def is_valid_attribute(attr, layer): + if ( + type(layer) == trt.IPoolingLayer + or type(layer) == trt.IConvolutionLayer + or type(layer) == trt.IDeconvolutionLayer + ): + if len(layer.get_input(0).shape) > 4: + # 3D pooling uses padding_nd + return attr not in ["padding", "stride", "window_size"] + if type(layer) == trt.IResizeLayer: + if layer.num_inputs > 1: + return attr not in ["scales"] + if type(layer) == trt.ISliceLayer: + if layer.num_inputs > 1: + return attr not in ["shape", "start", "stride"] + return True + + return [ + attr + for attr in dir(layer) + if not is_special_attribute(attr) and not hasattr(trt.ILayer, attr) and is_valid_attribute(attr, layer) + ] + + def str_from_network(network, mode="full"): """ Converts a TensorRT network to a human-readable representation @@ -164,31 +195,20 @@ def str_from_network(network, mode="full"): """ LAYER_TYPE_CLASS_MAPPING = get_layer_class_mapping() - def is_special_attribute(attr): - return attr.startswith("__") and attr.endswith("__") - - def is_valid_attribute(attr, layer): - if type(layer) == trt.IPoolingLayer or type(layer) == trt.IConvolutionLayer or type(layer) == trt.IDeconvolutionLayer: - if len(layer.get_input(0).shape) > 4: - # 3D pooling uses padding_nd - return attr not in ["padding", "stride", "window_size"] - if type(layer) == trt.IResizeLayer: - if layer.num_inputs > 1: - return attr not in ["scales"] - if type(layer) == trt.ISliceLayer: - if layer.num_inputs > 1: - return attr not in ["shape", "start", "stride"] - return True - - - network_str = "Name: {:} | {:} Batch Network{:}\n".format(network.name, - "Implicit" if hasattr(network, "has_implicit_batch_dimension") and network.has_implicit_batch_dimension else "Explicit", - " with Explicit Precision " if hasattr(network, "has_explicit_precision") and network.has_explicit_precision else "") + network_str = "Name: {:} | {:} Batch Network{:}\n".format( + network.name, + "Implicit" + if hasattr(network, "has_implicit_batch_dimension") and network.has_implicit_batch_dimension + else "Explicit", + " with Explicit Precision " + if hasattr(network, "has_explicit_precision") and network.has_explicit_precision + else "", + ) network_str += "\n" - input_metadata = get_input_metadata(network) + input_metadata = get_network_input_metadata(network) network_str += "---- {:} Network Input(s) ----\n{:}\n\n".format(len(input_metadata), input_metadata) - output_metadata = get_output_metadata(network) + output_metadata = get_network_output_metadata(network) network_str += "---- {:} Network Output(s) ----\n{:}\n\n".format(len(output_metadata), output_metadata) network_str += "---- {:} Layer(s) ----\n".format(network.num_layers) if mode != "none": @@ -200,7 +220,7 @@ def str_from_network(network, mode="full"): if mode in ["attrs", "full"]: # Exclude special attributes, as well as any attributes of the base layer class (those can be displayed above). - attrs = [attr for attr in dir(layer) if not is_special_attribute(attr) and not hasattr(trt.ILayer, attr) and is_valid_attribute(attr, layer)] + attrs = get_layer_attribute_names(layer) if attrs: network_str += util.indent_block("---- Attributes ----") + "\n" for attr in attrs: @@ -223,8 +243,10 @@ def _get_network_outputs(network): def check_outputs_not_found(not_found, available_outputs): if not_found: available_outputs = util.unique_list(available_outputs) - G_LOGGER.critical("The following outputs: {:} were not found. " - "Note: Available tensors: {:}".format(not_found, available_outputs)) + G_LOGGER.critical( + "The following outputs were not found: {:}.\n" + "Note: Available tensors:\n\t{:}".format(not_found, "\n\t".join(available_outputs)) + ) def mark_outputs(network, outputs): @@ -266,8 +288,11 @@ def mark_layerwise(network): in_loop = False for layer in network: if layer.type in LOOP_START_LAYERS: - G_LOGGER.warning("Loop detected. Please ensure the network is topologically sorted so that layers within " - "the loop body are not marked as network outputs in layerwise mode", mode=LogMode.ONCE) + G_LOGGER.warning( + "Loop detected. Please ensure the network is topologically sorted so that layers within " + "the loop body are not marked as network outputs in layerwise mode", + mode=LogMode.ONCE, + ) in_loop = True elif layer.type in LOOP_END_LAYERS: in_loop = False @@ -297,26 +322,36 @@ def unmark_outputs(network, outputs): def str_from_config(config): - config_str = "{:15} | {:} bytes ({:.2f} MiB)\n".format("Workspace", config.max_workspace_size, config.max_workspace_size / (1024.0 ** 2)) - config_str += "{:15} | ".format("Precision") + config_str = "{:20} | {:} bytes ({:.2f} MiB)\n".format( + "Workspace", config.max_workspace_size, config.max_workspace_size / (1024.0 ** 2) + ) + config_str += "{:20} | ".format("Precision") with contextlib.suppress(AttributeError): config_str += "TF32: {:}, ".format(config.get_flag(trt.BuilderFlag.TF32)) - config_str += "FP16: {:}, INT8: {:}, Strict Types: {:}\n".format(config.get_flag(trt.BuilderFlag.FP16), - config.get_flag(trt.BuilderFlag.INT8), config.get_flag(trt.BuilderFlag.STRICT_TYPES)) + config_str += "FP16: {:}, INT8: {:}, Strict Types: {:}\n".format( + config.get_flag(trt.BuilderFlag.FP16), + config.get_flag(trt.BuilderFlag.INT8), + config.get_flag(trt.BuilderFlag.STRICT_TYPES), + ) with contextlib.suppress(AttributeError): - source_vals = [val.name for val in trt.TacticSource.__members__.values() if (1 << int(val)) & config.get_tactic_sources()] - config_str += "{:15} | {:}\n".format("Tactic Sources", source_vals) + source_vals = [ + val.name for val in trt.TacticSource.__members__.values() if (1 << int(val)) & config.get_tactic_sources() + ] + config_str += "{:20} | {:}\n".format("Tactic Sources", source_vals) + + with contextlib.suppress(AttributeError): + config_str += "{:20}: {:}\n".format("Safety Restricted", config.get_flag(trt.BuilderFlag.SAFETY_SCOPE)) if config.int8_calibrator: - config_str += "{:15} | {:}\n".format("Calibrator", config.int8_calibrator) - config_str += "{:15} | {:} profile(s)".format("Profiles", config.num_optimization_profiles) + config_str += "{:20} | {:}\n".format("Calibrator", config.int8_calibrator) + config_str += "{:20} | {:} profile(s)".format("Profiles", config.num_optimization_profiles) return config_str def check_profile(profile): if not bool(profile): - G_LOGGER.critical("Profile is not valid, please provide profile data. Note: profile was: {:}".format(profile)) + G_LOGGER.critical("Profile is not valid, please provide profile data.\nNote: profile was: {:}".format(profile)) return profile @@ -354,44 +389,52 @@ def get_input_metadata_from_profile(profile, network): shapes = profile.get_shape(tensor.name) if tuple(shapes[0]) != tuple(shapes[2]): - G_LOGGER.warning("Will use `opt` shapes from profile 0 for calibration. " - "Note that even though `min` != `max` in this profile, calibration " - "will use fixed input shapes (this is not necessarily an issue).") + G_LOGGER.warning( + "Will use `opt` shapes from profile 0 for calibration. " + "Note that even though `min` != `max` in this profile, calibration " + "will use fixed input shapes (this is not necessarily an issue)." + ) # Always use opt shape input_metadata.add(name=tensor.name, dtype=trt.nptype(tensor.dtype), shape=shapes[1]) return input_metadata -def add_binding_to_metadata(engine, binding, metadata): +def add_binding_to_metadata(engine, binding, metadata, name_binding): + # name_binding always comes from profile 0, since that's where we + # get all binding names in the runner metadata.add( - name=engine[binding], + name=engine[name_binding], dtype=trt.nptype(engine.get_binding_dtype(binding)), - shape=list(engine.get_binding_shape(binding)) + shape=list(engine.get_binding_shape(binding)), ) def get_input_metadata_from_engine(engine, start_binding, end_binding): inputs = TensorMetadata() - for binding in range(start_binding, end_binding): + for index, binding in enumerate(range(start_binding, end_binding)): if engine.binding_is_input(binding): - add_binding_to_metadata(engine, binding, inputs) + add_binding_to_metadata(engine, binding, inputs, name_binding=index) return inputs def get_output_metadata_from_engine(engine, start_binding, end_binding): outputs = TensorMetadata() - for binding in range(start_binding, end_binding): + for index, binding in enumerate(range(start_binding, end_binding)): if not engine.binding_is_input(binding): - add_binding_to_metadata(engine, binding, outputs) + add_binding_to_metadata(engine, binding, outputs, name_binding=index) return outputs def str_from_engine(engine): bindings_per_profile = get_bindings_per_profile(engine) - engine_str = "Name: {:} | {:}{:} Batch Engine ({:} layers)\n".format(engine.name, - "Refittable " if engine.refittable else "", - "Implicit" if hasattr(engine, "has_implicit_batch_dimension") and engine.has_implicit_batch_dimension else "Explicit", - engine.num_layers) + engine_str = "Name: {:} | {:}{:} Batch Engine ({:} layers)\n".format( + engine.name, + "Refittable " if engine.refittable else "", + "Implicit" + if hasattr(engine, "has_implicit_batch_dimension") and engine.has_implicit_batch_dimension + else "Explicit", + engine.num_layers, + ) engine_str += "\n" # Show metadata for the first profile (i.e. the dynamic shapes) @@ -402,16 +445,21 @@ def str_from_engine(engine): engine_str += "---- Memory ----\nDevice Memory: {:} bytes\n\n".format(engine.device_memory_size) - engine_str += "---- {:} Profile(s) ({:} Binding(s) Each) ----\n".format(engine.num_optimization_profiles, bindings_per_profile) + engine_str += "---- {:} Profile(s) ({:} Binding(s) Each) ----\n".format( + engine.num_optimization_profiles, bindings_per_profile + ) for profile_index in range(engine.num_optimization_profiles): engine_str += "- Profile: {:}\n".format(profile_index) max_width = max([len(binding) for binding in engine]) + 8 for offset in range(bindings_per_profile): binding = profile_index * bindings_per_profile + offset - name = "[Name: {:}]".format(engine.get_binding_name(binding)) - engine_str += util.indent_block("Binding Index: {:} {:} {:<{max_width}}".format( - binding, "(Input) " if engine.binding_is_input(binding) else "(Output)", name, max_width=max_width)) + name = "[Name: {:}]".format(engine.get_binding_name(binding)) + engine_str += util.indent_block( + "Binding Index: {:} {:} {:<{max_width}}".format( + binding, "(Input) " if engine.binding_is_input(binding) else "(Output)", name, max_width=max_width + ) + ) if engine.binding_is_input(binding): if engine.is_shape_binding(binding): @@ -420,7 +468,7 @@ def str_from_engine(engine): min_shape, opt_shape, max_shape = engine.get_profile_shape(profile_index, binding) engine_str += " | Shapes: min={:}, opt={:}, max={:}\n".format(min_shape, opt_shape, max_shape) else: - engine_str += " | Shape: {:}".format(tuple(output_metadata[engine[offset]].shape)) + engine_str += " | Shape: {:}\n".format(engine.get_binding_shape(binding)) engine_str += "\n" return util.indent_block(engine_str, level=0) @@ -446,8 +494,10 @@ def get_active_profile_bindings(context): start_binding = bindings_per_profile * active_profile end_binding = start_binding + bindings_per_profile - G_LOGGER.ultra_verbose("Total # of Profiles: {:}, Bindings Per Profile: {:}, Active Profile: {:}, " - "Start Binding: {:}, End Binding: {:}".format( - context.engine.num_optimization_profiles, bindings_per_profile, - active_profile, start_binding, end_binding)) + G_LOGGER.ultra_verbose( + "Total # of Profiles: {:}, Bindings Per Profile: {:}, Active Profile: {:}, " + "Start Binding: {:}, End Binding: {:}".format( + context.engine.num_optimization_profiles, bindings_per_profile, active_profile, start_binding, end_binding + ) + ) return start_binding, end_binding diff --git a/tools/Polygraphy/polygraphy/backend/trt_legacy.py b/tools/Polygraphy/polygraphy/backend/trt_legacy.py index 83fd332b..f5ac1d2a 100644 --- a/tools/Polygraphy/polygraphy/backend/trt_legacy.py +++ b/tools/Polygraphy/polygraphy/backend/trt_legacy.py @@ -54,10 +54,11 @@ class ConvertToUff(BaseLoader): def call_impl(self): """ - save_uff (bool): Whether to write the generated UFF and corresponding PBTXT files. + save_uff (bool): Whether to write the generated UFF and corresponding PBTXT files. """ import uff from polygraphy.backend.tf import util as tf_util + G_LOGGER.module_info(uff) graph, output_names = self.tf_loader() @@ -66,11 +67,16 @@ class ConvertToUff(BaseLoader): output_filename = None if not self.uff_path else "out.uff" # Generate the UFF model and get information about the input_buffers/output_buffers. - uff_model, input_nodes, _ = uff.from_tensorflow(graph.as_graph_def(), return_graph_info=True, - quiet=(G_LOGGER.severity > G_LOGGER.VERBOSE), - debug_mode=(G_LOGGER.severity == G_LOGGER.EXTRA_VERBOSE), text=self.uff_path, - save_preprocessed=self.uff_path, output_filename=output_filename, - preprocessor=self.preprocessor) + uff_model, input_nodes, _ = uff.from_tensorflow( + graph.as_graph_def(), + return_graph_info=True, + quiet=(G_LOGGER.severity > G_LOGGER.VERBOSE), + debug_mode=(G_LOGGER.severity == G_LOGGER.EXTRA_VERBOSE), + text=self.uff_path, + save_preprocessed=self.uff_path, + output_filename=output_filename, + preprocessor=self.preprocessor, + ) input_names = [node.name for node in input_nodes] input_shapes = [tuple(int(dim.size) for dim in node.attr["shape"].shape.dim) for node in input_nodes] @@ -99,7 +105,9 @@ class LoadNetworkFromUff(BaseLoader): if FormatManager.determine_format(shape) == DataFormat.NHWC: input_order = trt.UffInputOrder.NHWC shape = shape[1:] - G_LOGGER.verbose("Registering UFF input: {:} with shape: {:} and input order: {:}".format(name, shape, input_order)) + G_LOGGER.verbose( + "Registering UFF input: {:} with shape: {:} and input order: {:}".format(name, shape, input_order) + ) parser.register_input(name, shape, input_order) if output_names and output_names != constants.MARK_ALL: @@ -126,7 +134,6 @@ class ParseNetworkFromOnnxLegacy(BaseNetworkFromOnnx): super().__init__(explicit_precision=False, explicit_batch=False) self.onnx_loader = onnx_loader - def call_impl(self): from polygraphy.backend.onnx import util as onnx_util @@ -145,12 +152,16 @@ class LoadNetworkFromCaffe(object): self.deploy = deploy self.model = model if not self.model: - G_LOGGER.warning("No model file provided for Caffe model, random weights will be used. To avoid this, " - "please set the model paramater, or --model") + G_LOGGER.warning( + "No model file provided for Caffe model, random weights will be used. To avoid this, " + "please set the model paramater, or --model" + ) if not outputs: - G_LOGGER.critical("Please set Caffe model outputs using the outputs parameter, or --trt-outputs. " - "Note: To determine possible outputs, try running: tail -n50 {:}".format(deploy)) + G_LOGGER.critical( + "Please set Caffe model outputs using the outputs parameter, or --trt-outputs. " + "Note: To determine possible outputs, try running: tail -n50 {:}".format(deploy) + ) self.outputs = outputs self.dtype = util.default(dtype, trt.float32) @@ -175,6 +186,7 @@ class TrtLegacyRunner(BaseRunner): """ A runner that can perform inference on a single TensorRT engine. """ + # Simple helper data class that's a little nicer to use than a 2-tuple. class HostDeviceMem(object): def __init__(self, host_mem, device_mem): @@ -184,8 +196,19 @@ class TrtLegacyRunner(BaseRunner): def __str__(self): return "Host:" + str(self.host) + ", Device:" + str(self.device) - def __init__(self, network_loader=None, max_workspace_size=None, max_batch_size=None, fp16=None, - tf32=None, load_engine=None, save_engine=None, layerwise=False, plugins=[], name=None): + def __init__( + self, + network_loader=None, + max_workspace_size=None, + max_batch_size=None, + fp16=None, + tf32=None, + load_engine=None, + save_engine=None, + layerwise=False, + plugins=[], + name=None, + ): """ Creates a runner that manages a single TensorRT engine. @@ -204,6 +227,7 @@ class TrtLegacyRunner(BaseRunner): # Load any user-supplied plugin libraries. This must happen before everything else, including engine deserialization. if plugins: import ctypes + for plugin in plugins: path = os.path.abspath(plugin) G_LOGGER.info("Loading plugin library: {:}".format(path)) @@ -214,7 +238,7 @@ class TrtLegacyRunner(BaseRunner): # Save parameters for activate and deactivate. self.network_loader = network_loader - self.max_workspace_size = util.default(max_workspace_size, 1<<24) + self.max_workspace_size = util.default(max_workspace_size, 1 << 24) self.fp16 = util.default(fp16, False) self.tf32 = util.default(tf32, False) self.load_engine = load_engine @@ -224,7 +248,6 @@ class TrtLegacyRunner(BaseRunner): self.layerwise = layerwise self.max_batch_size = max_batch_size - def activate_impl(self): """ Vars: @@ -249,12 +272,11 @@ class TrtLegacyRunner(BaseRunner): stream = cuda.Stream() G_LOGGER.verbose("Using batch size: " + str(engine.max_batch_size) + " during buffer allocation") for binding in engine: - shape = (engine.max_batch_size, ) + tuple(engine.get_binding_shape(binding)) + shape = (engine.max_batch_size,) + tuple(engine.get_binding_shape(binding)) dtype = engine.get_binding_dtype(binding) device_mem = cuda.DeviceArray(shape=shape, dtype=trt.nptype(dtype)) - G_LOGGER.extra_verbose("Tensor: " - "{:35} | Allocated: {:}".format(binding, device_mem)) + G_LOGGER.extra_verbose("Tensor: " "{:35} | Allocated: {:}".format(binding, device_mem)) if engine.binding_is_input(binding): input_buffers[binding] = TrtLegacyRunner.HostDeviceMem(None, device_mem) @@ -277,7 +299,8 @@ class TrtLegacyRunner(BaseRunner): config.max_workspace_size = int(self.max_workspace_size) if not self.tf32: - with contextlib.suppress(AttributeError): config.clear_flag(trt.BuilderFlag.TF32) + with contextlib.suppress(AttributeError): + config.clear_flag(trt.BuilderFlag.TF32) if self.fp16: config.flags = 1 << int(trt.BuilderFlag.FP16) @@ -285,7 +308,6 @@ class TrtLegacyRunner(BaseRunner): G_LOGGER.critical("Invalid network") G_LOGGER.super_verbose(lambda: trt_util.str_from_network(network) or "Finished logging network") - if self.layerwise: # In layerwise mode, every layer becomes an output. G_LOGGER.info("Running in layerwise mode. Marking {:} layers as outputs".format(network.num_layers)) @@ -295,11 +317,12 @@ class TrtLegacyRunner(BaseRunner): if not out.is_network_output: network.mark_output(out) - G_LOGGER.info("Building engine: max workspace size={:} bytes, max batch size={:}, fp16={:}, " - "tf32={:}".format(config.max_workspace_size, builder.max_batch_size, self.fp16, self.tf32)) + G_LOGGER.info( + "Building engine: max workspace size={:} bytes, max batch size={:}, fp16={:}, " + "tf32={:}".format(config.max_workspace_size, builder.max_batch_size, self.fp16, self.tf32) + ) self.engine = builder.build_engine(network, config) - if not self.engine: G_LOGGER.critical("Invalid Engine. Please ensure the engine was built correctly") @@ -311,17 +334,19 @@ class TrtLegacyRunner(BaseRunner): self.context = self.engine.create_execution_context() self.input_buffers, self.output_buffers, self.stream = allocate_buffers(self.engine) - def get_input_metadata_impl(self): inputs = TensorMetadata() for binding in self.engine: if self.engine.binding_is_input(binding): # Always prepend a dynamic batch dimension - inputs.add(binding, trt.nptype(self.engine.get_binding_dtype(binding)), [-1] + list(self.engine.get_binding_shape(binding))) + inputs.add( + binding, + trt.nptype(self.engine.get_binding_dtype(binding)), + [-1] + list(self.engine.get_binding_shape(binding)), + ) return inputs - def deactivate_impl(self): # Destroy the engine and context. with self.engine, self.context: @@ -332,14 +357,16 @@ class TrtLegacyRunner(BaseRunner): del (self.engine, self.context, self.input_buffers, self.output_buffers, self.stream) - def infer_impl(self, feed_dict): start = time.time() [self.input_buffers[name].device.copy_from(buffer, self.stream) for name, buffer in feed_dict.items()] # We will not run with smaller batch sizes than whatever the builder chose. - bindings = [buf.device.ptr for buf in self.input_buffers.values()] + [buf.device.ptr for buf in self.output_buffers.values()] - status = self.context.execute_async(batch_size=self.context.engine.max_batch_size, bindings=bindings, - stream_handle=self.stream.ptr) + bindings = [buf.device.ptr for buf in self.input_buffers.values()] + [ + buf.device.ptr for buf in self.output_buffers.values() + ] + status = self.context.execute_async( + batch_size=self.context.engine.max_batch_size, bindings=bindings, stream_handle=self.stream.ptr + ) if not status: G_LOGGER.critical("Model execution failed. Please see the log messages above for details") diff --git a/tools/Polygraphy/polygraphy/common/interface.py b/tools/Polygraphy/polygraphy/common/interface.py index 6f01df12..a519a64f 100644 --- a/tools/Polygraphy/polygraphy/common/interface.py +++ b/tools/Polygraphy/polygraphy/common/interface.py @@ -37,21 +37,26 @@ def TypedDict(key_type_func, value_type_func): value_type_func (Callable() -> type): A callable that returns the expected value type. """ + class Interface(object): def __init__(self, dct=None): self.dct = OrderedDict(util.default(dct, {})) self.key_type = key_type_func() self.value_type = value_type_func() - def _check_types(self, key, val): if not isinstance(key, self.key_type): - G_LOGGER.critical("Unsupported key type in {:}. Key: {:} is type `{:}` but {:} expects type `{:}`".format( - self, repr(key), type(key).__name__, type(self).__name__, self.key_type.__name__)) + G_LOGGER.critical( + "Unsupported key type in {:}. Key: {:} is type `{:}` but {:} expects type `{:}`".format( + self, repr(key), type(key).__name__, type(self).__name__, self.key_type.__name__ + ) + ) if not isinstance(val, self.value_type): - G_LOGGER.critical("Unsupported value type in {:}. Value: {:} for key: {:} is type `{:}` but {:} expects type `{:}`".format( - self, repr(val), repr(key), type(val).__name__, type(self).__name__, self.value_type.__name__)) - + G_LOGGER.critical( + "Unsupported value type in {:}. Value: {:} for key: {:} is type `{:}` but {:} expects type `{:}`".format( + self, repr(val), repr(key), type(val).__name__, type(self).__name__, self.value_type.__name__ + ) + ) def keys(self): return self.dct.keys() @@ -125,12 +130,13 @@ def TypedList(elem_type_func): self.lst = util.default(lst, []) self.elem_type = elem_type_func() - def _check_type(self, elem): if not isinstance(elem, self.elem_type): - G_LOGGER.critical("Unsupported element type type in {:}. Element: {:} is type: {:} but type: {:} was expected".format( - type(self).__name__, repr(elem), type(elem).__name__, self.elem_type.__name__)) - + G_LOGGER.critical( + "Unsupported element type type in {:}. Element: {:} is type: {:} but type: {:} was expected".format( + type(self).__name__, repr(elem), type(elem).__name__, self.elem_type.__name__ + ) + ) def __contains__(self, key): return key in self.lst diff --git a/tools/Polygraphy/polygraphy/common/struct.py b/tools/Polygraphy/polygraphy/common/struct.py index fb75edc8..d764fc07 100644 --- a/tools/Polygraphy/polygraphy/common/struct.py +++ b/tools/Polygraphy/polygraphy/common/struct.py @@ -25,14 +25,23 @@ class MetadataTuple(object): self.dtype = dtype self.shape = shape - def __iter__(self): yield from [self.dtype, self.shape] - def __repr__(self): return "MetadataTuple({:}, {:})".format(self.dtype, self.shape) + def __str__(self): + ret = "" + meta_items = [] + if self.dtype is not None: + meta_items.append("dtype={:}".format(np.dtype(self.dtype).name)) + if self.shape is not None: + meta_items.append("shape={:}".format(tuple(self.shape))) + if meta_items: + ret += "[" + ", ".join(meta_items) + "]" + return ret + @mod.export() class TensorMetadata(TypedDict(lambda: str, lambda: MetadataTuple)): @@ -47,6 +56,7 @@ class TensorMetadata(TypedDict(lambda: str, lambda: MetadataTuple)): shape = tensor_meta["input0"].shape dtype = tensor_meta["input0"].dtype """ + @staticmethod def from_feed_dict(feed_dict): """ @@ -64,7 +74,6 @@ class TensorMetadata(TypedDict(lambda: str, lambda: MetadataTuple)): meta.add(name, arr.dtype, arr.shape) return meta - def add(self, name, dtype, shape): """ Convenience function for adding entries. @@ -82,26 +91,13 @@ class TensorMetadata(TypedDict(lambda: str, lambda: MetadataTuple)): self[name] = MetadataTuple(dtype, shape) return self - def __repr__(self): ret = "TensorMetadata()" for name, (dtype, shape) in self.items(): ret += ".add('{:}', {:}, {:})".format(name, dtype, shape) return ret - def __str__(self): - def str_from_single_meta(name, dtype, shape): - ret = "{:}".format(name) - meta_items = [] - if dtype is not None: - meta_items.append("dtype={:}".format(np.dtype(dtype).name)) - if shape is not None: - meta_items.append("shape={:}".format(tuple(shape))) - if meta_items: - ret += " [" + ", ".join(meta_items) + "]" - return ret - sep = ",\n " - elems = [str_from_single_meta(name, dtype, shape) for name, (dtype, shape) in self.items()] + elems = ["{:} {:}".format(name, meta_tuple).strip() for name, meta_tuple in self.items()] return "{" + sep.join(elems) + "}" diff --git a/tools/Polygraphy/polygraphy/comparator/comparator.py b/tools/Polygraphy/polygraphy/comparator/comparator.py index 034a06fe..f5f2814a 100644 --- a/tools/Polygraphy/polygraphy/comparator/comparator.py +++ b/tools/Polygraphy/polygraphy/comparator/comparator.py @@ -23,8 +23,7 @@ from polygraphy.common import TensorMetadata from polygraphy.comparator import util as comp_util from polygraphy.comparator.compare import CompareFunc from polygraphy.comparator.data_loader import DataLoader, DataLoaderCache -from polygraphy.comparator.struct import (AccuracyResult, IterationResult, - RunResults) +from polygraphy.comparator.struct import AccuracyResult, IterationResult, RunResults from polygraphy.logger import G_LOGGER, LogMode np = mod.lazy_import("numpy") @@ -35,10 +34,17 @@ class Comparator(object): """ Compares inference outputs. """ + @staticmethod - def run(runners, data_loader=None, warm_up=None, - use_subprocess=None, subprocess_timeout=None, - subprocess_polling_interval=None, save_inputs_path=None): + def run( + runners, + data_loader=None, + warm_up=None, + use_subprocess=None, + subprocess_timeout=None, + subprocess_polling_interval=None, + save_inputs_path=None, + ): """ Runs the supplied runners sequentially. @@ -87,12 +93,12 @@ class Comparator(object): subprocess_polling_interval = util.default(subprocess_polling_interval, 30) loader_cache = DataLoaderCache(data_loader, save_inputs_path=save_inputs_path) - def execute_runner(runner, loader_cache): with runner as active_runner: input_metadata = active_runner.get_input_metadata() - G_LOGGER.info("{:35}\n---- Model Input(s) ----\n{:}".format(active_runner.name, input_metadata), - mode=LogMode.ONCE) + G_LOGGER.info( + "{:35}\n---- Model Input(s) ----\n{:}".format(active_runner.name, input_metadata), mode=LogMode.ONCE + ) # DataLoaderCache will ensure that the feed_dict does not contain any extra entries # based on the provided input_metadata. @@ -103,8 +109,10 @@ class Comparator(object): try: feed_dict = loader_cache[0] except IndexError: - G_LOGGER.warning("{:} warm-up run(s) were requested, but data loader did not supply any data. " - "Skipping warm-up run(s)".format(warm_up)) + G_LOGGER.warning( + "{:} warm-up run(s) were requested, but data loader did not supply any data. " + "Skipping warm-up run(s)".format(warm_up) + ) else: G_LOGGER.ultra_verbose("Warm-up Input Buffers:\n{:}".format(util.indent_block(feed_dict))) # First do a few warm-up runs, and don't time them. @@ -118,25 +126,38 @@ class Comparator(object): total_runtime = 0 for index, feed_dict in enumerate(loader_cache): - G_LOGGER.extra_verbose(lambda: "{:35} | Feeding inputs:\n{:}".format(active_runner.name, util.indent_block(feed_dict))) + G_LOGGER.extra_verbose( + lambda: "{:35} | Feeding inputs:\n{:}".format(active_runner.name, util.indent_block(feed_dict)) + ) outputs = active_runner.infer(feed_dict=feed_dict) runtime = active_runner.last_inference_time() total_runtime += runtime # Without a deep copy here, outputs will always reference the output of the last run - iteration_results.append(IterationResult(outputs=copy.deepcopy(outputs), runtime=runtime, runner_name=active_runner.name)) + iteration_results.append( + IterationResult(outputs=copy.deepcopy(outputs), runtime=runtime, runner_name=active_runner.name) + ) - G_LOGGER.info(lambda: "{:35}\n---- Model Output(s) ----\n{:}".format( - active_runner.name, TensorMetadata().from_feed_dict(outputs)), - mode=LogMode.ONCE) - G_LOGGER.extra_verbose(lambda: "{:35} | Inference Time: {:.3f} ms | Received outputs:\n{:}".format( - active_runner.name, runtime * 1000.0, util.indent_block(outputs))) + G_LOGGER.info( + "{:35}\n---- Model Output(s) ----\n{:}".format( + active_runner.name, TensorMetadata().from_feed_dict(outputs) + ), + mode=LogMode.ONCE, + ) + G_LOGGER.extra_verbose( + lambda: "{:35} | Inference Time: {:.3f} ms | Received outputs:\n{:}".format( + active_runner.name, runtime * 1000.0, util.indent_block(outputs) + ) + ) total_runtime_ms = total_runtime * 1000.0 - G_LOGGER.finish("{:35} | Completed {:} iteration(s) in {:.4g} ms | Average inference time: {:.4g} ms.".format(active_runner.name, index + 1, total_runtime_ms, total_runtime_ms / float(index + 1))) + G_LOGGER.finish( + "{:35} | Completed {:} iteration(s) in {:.4g} ms | Average inference time: {:.4g} ms.".format( + active_runner.name, index + 1, total_runtime_ms, total_runtime_ms / float(index + 1) + ) + ) return iteration_results - # Wraps execute_runner to use a queue. def execute_runner_with_queue(runner_queue, runner, loader_cache): iteration_results = None @@ -149,13 +170,14 @@ class Comparator(object): # After finishing, send the updated loader_cache back. util.try_send_on_queue(runner_queue, loader_cache) - # Do all inferences in one loop, then comparisons at a later stage. # We run each runner in a separate process so that we can provide exclusive GPU access for each runner. run_results = RunResults() if not runners: - G_LOGGER.warning("No runners were provided to Comparator.run(). Inference will not be run, and run results will be empty.") + G_LOGGER.warning( + "No runners were provided to Comparator.run(). Inference will not be run, and run results will be empty." + ) for runner in runners: G_LOGGER.start("{:35} | Activating and starting inference".format(runner.name)) @@ -169,7 +191,9 @@ class Comparator(object): iteration_results = None while process.is_alive() and iteration_results is None: try: - iteration_results = util.try_receive_on_queue(runner_queue, timeout=subprocess_polling_interval / 2) + iteration_results = util.try_receive_on_queue( + runner_queue, timeout=subprocess_polling_interval / 2 + ) # Receive updated loader cache, or fall back if it could not be sent. loader_cache = util.try_receive_on_queue(runner_queue, timeout=subprocess_polling_interval / 2) except queue.Empty: @@ -180,22 +204,25 @@ class Comparator(object): run_results.append((runner.name, iteration_results)) process.join(subprocess_timeout) except: - G_LOGGER.critical("{:35} | Terminated prematurely. Check the exception logged above. " - "If there is no exception logged above, make sure not to use the --use-subprocess " - "flag or set use_subprocess=False in Comparator.run().".format(runner.name)) + G_LOGGER.critical( + "{:35} | Terminated prematurely. Check the exception logged above. " + "If there is no exception logged above, make sure not to use the --use-subprocess " + "flag or set use_subprocess=False in Comparator.run().".format(runner.name) + ) finally: process.terminate() if loader_cache is None: - G_LOGGER.critical("Could not send data loader cache to runner subprocess. Please try disabling subprocesses " - "by removing the --use-subprocess flag, or setting use_subprocess=False in Comparator.run()") + G_LOGGER.critical( + "Could not send data loader cache to runner subprocess. Please try disabling subprocesses " + "by removing the --use-subprocess flag, or setting use_subprocess=False in Comparator.run()" + ) else: run_results.append((runner.name, execute_runner(runner, loader_cache))) G_LOGGER.verbose("Successfully ran: {:}".format([r.name for r in runners])) return run_results - @staticmethod def postprocess(run_results, postprocess_func): """ @@ -215,13 +242,11 @@ class Comparator(object): iteration_results[index] = postprocess_func(iter_res) return run_results - @staticmethod def default_comparisons(run_results): # Sets up default comparisons - which is to compare each runner to the subsequent one. return [(i, i + 1) for i in range(len(run_results) - 1)] - @staticmethod def compare_accuracy(run_results, fail_fast=False, comparisons=None, compare_func=None): """ @@ -246,6 +271,7 @@ class Comparator(object): guaranteed to be the same as the order of `comparisons`. For more details, see the AccuracyResult docstring (e.g. help(AccuracyResult)). """ + def find_mismatched(match_dict): return [name for name, matched in match_dict.items() if not bool(matched)] @@ -275,20 +301,25 @@ class Comparator(object): if fail_fast and mismatched_outputs: return accuracy_result - G_LOGGER.extra_verbose("Finished comparing {:} with {:}".format(runner0_name, runner1_name,)) + G_LOGGER.extra_verbose( + "Finished comparing {:} with {:}".format( + runner0_name, + runner1_name, + ) + ) passed, _, total = accuracy_result.stats(runner_pair) pass_rate = accuracy_result.percentage(runner_pair) * 100.0 if num_iters > 1 or len(comparisons) > 1: msg = "Accuracy Summary | {:} vs. {:} | Passed: {:}/{:} iterations | Pass Rate: {:}%".format( - runner0_name, runner1_name, passed, total, pass_rate) + runner0_name, runner1_name, passed, total, pass_rate + ) if passed == total: G_LOGGER.finish(msg) else: G_LOGGER.error(msg) return accuracy_result - @staticmethod def validate(run_results, check_inf=None, check_nan=None, fail_fast=None): """ @@ -307,31 +338,37 @@ class Comparator(object): check_nan = util.default(check_nan, True) fail_fast = util.default(fail_fast, False) - def is_finite(output): non_finite = np.logical_not(np.isfinite(output)) if np.any(non_finite): G_LOGGER.error("Inf Detected | One or more non-finite values were encountered in this output") - G_LOGGER.info("Note: Use -vv or set logging verbosity to EXTRA_VERBOSE to display non-finite values", mode=LogMode.ONCE) + G_LOGGER.info( + "Note: Use -vv or set logging verbosity to EXTRA_VERBOSE to display non-finite values", + mode=LogMode.ONCE, + ) G_LOGGER.extra_verbose("Note: non-finite values at:\n{:}".format(non_finite)) G_LOGGER.extra_verbose("Note: non-finite values:\n{:}".format(output[non_finite])) return False return True - def is_not_nan(output): nans = np.isnan(output) if np.any(nans): G_LOGGER.error("NaN Detected | One or more NaNs were encountered in this output") - G_LOGGER.info("Note: Use -vv or set logging verbosity to EXTRA_VERBOSE to display locations of NaNs", mode=LogMode.ONCE) + G_LOGGER.info( + "Note: Use -vv or set logging verbosity to EXTRA_VERBOSE to display locations of NaNs", + mode=LogMode.ONCE, + ) G_LOGGER.extra_verbose("Note: NaNs at:\n{:}".format(nans)) return False return True - def validate_output(runner_name, output_name, output): - G_LOGGER.start("{:35} | Validating output: {:} (check_inf={:}, check_nan={:})".format( - runner_name, output_name, check_inf, check_nan)) + G_LOGGER.start( + "{:35} | Validating output: {:} (check_inf={:}, check_nan={:})".format( + runner_name, output_name, check_inf, check_nan + ) + ) with G_LOGGER.indent(): comp_util.log_output_stats(output) @@ -347,7 +384,6 @@ class Comparator(object): G_LOGGER.error("FAILED | Errors detected in output: {:}".format(output_name)) return output_valid - all_valid = True G_LOGGER.start("Output Validation | Runners: {:}".format(list(run_results.keys()))) with G_LOGGER.indent(): diff --git a/tools/Polygraphy/polygraphy/comparator/compare.py b/tools/Polygraphy/polygraphy/comparator/compare.py index 559c5cbc..d6cdb2d0 100644 --- a/tools/Polygraphy/polygraphy/comparator/compare.py +++ b/tools/Polygraphy/polygraphy/comparator/compare.py @@ -29,10 +29,8 @@ class OutputCompareResult(object): Represents the result of comparing a single output of a single iteration between two runners. """ - def __init__(self, passed, - max_absdiff, max_reldiff, - mean_absdiff, mean_reldiff, - median_absdiff, median_reldiff): + + def __init__(self, passed, max_absdiff, max_reldiff, mean_absdiff, mean_reldiff, median_absdiff, median_reldiff): """ Records the required tolerances and other statistics gathered during comparison. @@ -60,7 +58,6 @@ class OutputCompareResult(object): self.median_absdiff = median_absdiff self.median_reldiff = median_reldiff - def __bool__(self): """ Whether the output matched. @@ -70,7 +67,6 @@ class OutputCompareResult(object): """ return self.passed - def __str__(self): return "(atol={:}, rtol={:})".format(self.max_absdiff, self.max_reldiff) @@ -83,8 +79,9 @@ class CompareFunc(object): """ @staticmethod - def basic_compare_func(check_shapes=None, rtol=None, atol=None, fail_fast=None, - find_output_func=None, check_error_stat=None): + def basic_compare_func( + check_shapes=None, rtol=None, atol=None, fail_fast=None, find_output_func=None, check_error_stat=None + ): """ Creates a function that compares two IterationResults, and can be used as the `compare_func` argument in ``Comparator.compare_accuracy``. @@ -140,7 +137,6 @@ class CompareFunc(object): default_error_stat = "elemwise" check_error_stat = util.default(check_error_stat, default_error_stat) - def compare_output(iter_result0, iter_result1): """ Compare the outputs of two runners from a single iteration. @@ -162,28 +158,39 @@ class CompareFunc(object): Raises: PolygraphyException: If all output names are skipped, and thus no outputs are compared. """ + def check_dict(dct, dict_name): if isinstance(dct, dict): - util.check_dict_contains(dct, set(iter_result0.keys()) | set(iter_result1.keys()) | set([""]), - check_missing=False, dict_name=dict_name) - + util.check_dict_contains( + dct, + set(iter_result0.keys()) | set(iter_result1.keys()) | {""}, + check_missing=False, + dict_name=dict_name, + ) check_dict(rtol, "the rtol dictionary") check_dict(atol, "the atol dictionary") check_dict(check_error_stat, "the chcek_error_stat dictionary") - # Returns whether the outputs match def check_outputs_match(out0, out0_name, out1, out1_name, per_out_rtol, per_out_atol, per_out_err_stat): VALID_CHECK_ERROR_STATS = ["max", "mean", "median", "elemwise"] if per_out_err_stat not in VALID_CHECK_ERROR_STATS: - G_LOGGER.critical("Invalid choice for check_error_stat: {:}.\n" - "Note: Valid choices are: {:}".format(per_out_err_stat, VALID_CHECK_ERROR_STATS)) + G_LOGGER.critical( + "Invalid choice for check_error_stat: {:}.\n" + "Note: Valid choices are: {:}".format(per_out_err_stat, VALID_CHECK_ERROR_STATS) + ) - G_LOGGER.super_verbose("{:35} | Output: {:} (dtype={:}, shape={:}):\n{:}".format( - iter_result0.runner_name, out0_name, out0.dtype, out0.shape, util.indent_block(out0))) - G_LOGGER.super_verbose("{:35} | Output: {:} (dtype={:}, shape={:}):\n{:}".format( - iter_result1.runner_name, out1_name, out1.dtype, out1.shape, util.indent_block(out1))) + G_LOGGER.super_verbose( + "{:35} | Output: {:} (dtype={:}, shape={:}):\n{:}".format( + iter_result0.runner_name, out0_name, out0.dtype, out0.shape, util.indent_block(out0) + ) + ) + G_LOGGER.super_verbose( + "{:35} | Output: {:} (dtype={:}, shape={:}):\n{:}".format( + iter_result1.runner_name, out1_name, out1.dtype, out1.shape, util.indent_block(out1) + ) + ) # Check difference vs. tolerances if np.issubdtype(out0.dtype, np.bool_) and np.issubdtype(out1.dtype, np.bool_): @@ -209,11 +216,15 @@ class CompareFunc(object): if per_out_err_stat == "mean": failed = mean_absdiff > per_out_atol and (np.isnan(mean_reldiff) or mean_reldiff > per_out_rtol) elif per_out_err_stat == "median": - failed = median_absdiff > per_out_atol and (np.isnan(median_reldiff) or median_reldiff > per_out_rtol) + failed = median_absdiff > per_out_atol and ( + np.isnan(median_reldiff) or median_reldiff > per_out_rtol + ) elif per_out_err_stat == "max": failed = max_absdiff > per_out_atol and (np.isnan(max_reldiff) or max_reldiff > per_out_rtol) else: - assert per_out_err_stat == "elemwise", "This branch should be unreachable unless per_out_err_stat is 'elemwise'" + assert ( + per_out_err_stat == "elemwise" + ), "This branch should be unreachable unless per_out_err_stat is 'elemwise'" mismatches = (absdiff > per_out_atol) & (reldiff > per_out_rtol) failed = np.any(mismatches) @@ -224,19 +235,30 @@ class CompareFunc(object): with G_LOGGER.indent(): G_LOGGER.super_verbose("Mismatched indices:\n{:}".format(np.argwhere(mismatches))) - G_LOGGER.extra_verbose("{:35} | Mismatched values:\n{:}".format(iter_result0.runner_name, out0[mismatches])) - G_LOGGER.extra_verbose("{:35} | Mismatched values:\n{:}".format(iter_result1.runner_name, out1[mismatches])) + G_LOGGER.extra_verbose( + "{:35} | Mismatched values:\n{:}".format(iter_result0.runner_name, out0[mismatches]) + ) + G_LOGGER.extra_verbose( + "{:35} | Mismatched values:\n{:}".format(iter_result1.runner_name, out1[mismatches]) + ) except Exception as err: G_LOGGER.warning("Failing to log mismatches.\nNote: Error was: {:}".format(err)) # Log information about the outputs - hist_bin_range = (min(comp_util.compute_min(out0), comp_util.compute_min(out1)), - max(comp_util.compute_max(out0), comp_util.compute_max(out1))) - comp_util.log_output_stats(out0, failed, iter_result0.runner_name + ": " + out0_name, hist_range=hist_bin_range) - comp_util.log_output_stats(out1, failed, iter_result1.runner_name + ": " + out1_name, hist_range=hist_bin_range) + hist_bin_range = ( + min(comp_util.compute_min(out0), comp_util.compute_min(out1)), + max(comp_util.compute_max(out0), comp_util.compute_max(out1)), + ) + comp_util.log_output_stats( + out0, failed, iter_result0.runner_name + ": " + out0_name, hist_range=hist_bin_range + ) + comp_util.log_output_stats( + out1, failed, iter_result1.runner_name + ": " + out1_name, hist_range=hist_bin_range + ) G_LOGGER.info("Error Metrics: {:}".format(out0_name)) with G_LOGGER.indent(): + def req_tol(mean_diff, median_diff, max_diff, elemwise_diff): return { "mean": mean_diff, @@ -245,32 +267,50 @@ class CompareFunc(object): "elemwise": elemwise_diff, }[per_out_err_stat] - G_LOGGER.info("Minimum Required Tolerance: {:} error | [abs={:.5g}] OR [rel={:.5g}]".format( - per_out_err_stat, - req_tol(mean_absdiff, median_absdiff, max_absdiff, max_elemwiseabs), - req_tol(mean_reldiff, median_reldiff, max_reldiff, max_elemwiserel))) + G_LOGGER.info( + "Minimum Required Tolerance: {:} error | [abs={:.5g}] OR [rel={:.5g}]".format( + per_out_err_stat, + req_tol(mean_absdiff, median_absdiff, max_absdiff, max_elemwiseabs), + req_tol(mean_reldiff, median_reldiff, max_reldiff, max_elemwiserel), + ) + ) comp_util.log_output_stats(absdiff, failed, "Absolute Difference") comp_util.log_output_stats(reldiff, failed, "Relative Difference") # Finally show summary. if failed: - G_LOGGER.error("FAILED | Difference exceeds tolerance (rel={:}, abs={:})".format(per_out_rtol, per_out_atol)) + G_LOGGER.error( + "FAILED | Difference exceeds tolerance (rel={:}, abs={:})".format(per_out_rtol, per_out_atol) + ) else: - G_LOGGER.finish("PASSED | Difference is within tolerance (rel={:}, abs={:})".format(per_out_rtol, per_out_atol)) + G_LOGGER.finish( + "PASSED | Difference is within tolerance (rel={:}, abs={:})".format(per_out_rtol, per_out_atol) + ) - G_LOGGER.extra_verbose("Finished comparing: '{:}' (dtype={:}, shape={:}) [{:}] and '{:}' (dtype={:}, shape={:}) [{:}]" - .format(out0_name, out0.dtype, out0.shape, iter_result0.runner_name, out1_name, out1.dtype, out1.shape, iter_result1.runner_name)) - return OutputCompareResult(not failed, max_absdiff, max_reldiff, mean_absdiff, mean_reldiff, median_absdiff, median_reldiff) + G_LOGGER.extra_verbose( + "Finished comparing: '{:}' (dtype={:}, shape={:}) [{:}] and '{:}' (dtype={:}, shape={:}) [{:}]".format( + out0_name, + out0.dtype, + out0.shape, + iter_result0.runner_name, + out1_name, + out1.dtype, + out1.shape, + iter_result1.runner_name, + ) + ) + return OutputCompareResult( + not failed, max_absdiff, max_reldiff, mean_absdiff, mean_reldiff, median_absdiff, median_reldiff + ) # # End: def check_outputs_match # - output_status = OrderedDict() # OrderedDict[str, bool] Maps output names to whether they matched. + output_status = OrderedDict() # OrderedDict[str, bool] Maps output names to whether they matched. if not check_shapes: G_LOGGER.info("Strict shape checking disabled. Will attempt to match output shapes before comparisons") - def default_find_output_func(output_name, index, iter_result): found_name = util.find_in_dict(output_name, iter_result, index) if found_name is None: @@ -278,15 +318,18 @@ class CompareFunc(object): elif found_name != output_name: exact_match = util.find_in_dict(found_name, iter_result0) if exact_match == found_name: - G_LOGGER.verbose("Will not compare {:} with {:}, since the former already has an exact match: {:}".format( - found_name, output_name, exact_match)) - return None # If the found output is being compared against another output already, skip this non-exact match - G_LOGGER.warning("Output names did not match exactly. Assuming {:} output: {:} " - "corresponds to output: {:}".format( - iter_result.runner_name, found_name, output_name)) + G_LOGGER.verbose( + "Will not compare {:} with {:}, since the former already has an exact match: {:}".format( + found_name, output_name, exact_match + ) + ) + return None # If the found output is being compared against another output already, skip this non-exact match + G_LOGGER.warning( + "Output names did not match exactly. Assuming {:} output: {:} " + "corresponds to output: {:}".format(iter_result.runner_name, found_name, output_name) + ) return [found_name] - nonlocal find_output_func find_output_func = util.default(find_output_func, default_find_output_func) @@ -294,17 +337,22 @@ class CompareFunc(object): out1_names = util.default(find_output_func(out0_name, index, iter_result1), []) if len(out1_names) > 1: - G_LOGGER.info("Will attempt to compare output: '{:}' [{:}] with multiple outputs: '{:}' [{:}]".format( - out0_name, iter_result0.runner_name, list(out1_names), iter_result1.runner_name)) + G_LOGGER.info( + "Will attempt to compare output: '{:}' [{:}] with multiple outputs: '{:}' [{:}]".format( + out0_name, iter_result0.runner_name, list(out1_names), iter_result1.runner_name + ) + ) for out1_name in out1_names: if out1_name is None or out1_name not in iter_result1: - G_LOGGER.warning("For output: '{:}' [{:}], skipping corresponding output: '{:}' [{:}], " - "since the output was not found".format(out0_name, iter_result0.runner_name, - out1_name, iter_result1.runner_name)) + G_LOGGER.warning( + "For output: '{:}' [{:}], skipping corresponding output: '{:}' [{:}], " + "since the output was not found".format( + out0_name, iter_result0.runner_name, out1_name, iter_result1.runner_name + ) + ) continue - def get_tol(tol_dict, default): if isinstance(tol_dict, numbers.Number): return tol_dict @@ -315,7 +363,6 @@ class CompareFunc(object): return tol_dict[""] return default - def get_error_stat(): if isinstance(check_error_stat, str): return check_error_stat @@ -323,42 +370,61 @@ class CompareFunc(object): if out0_name in check_error_stat: return check_error_stat[out0_name] elif "" in check_error_stat: - return check_error_stat[""] + return check_error_stat[""] return default_error_stat - per_out_atol = get_tol(atol, default_atol) per_out_rtol = get_tol(rtol, default_rtol) per_out_err_stat = get_error_stat() output1 = iter_result1[out1_name] - G_LOGGER.start("Comparing Output: '{:}' (dtype={:}, shape={:}) with '{:}' (dtype={:}, shape={:}) | " - "Tolerance: [abs={:.5g}, rel={:.5g}] | Checking {:} error".format( - out0_name, output0.dtype, output0.shape, - out1_name, output1.dtype, output1.shape, - per_out_atol, per_out_rtol, per_out_err_stat)) - G_LOGGER.extra_verbose("Note: Comparing {:} vs. {:}".format(iter_result0.runner_name, iter_result1.runner_name)) - + G_LOGGER.start( + "Comparing Output: '{:}' (dtype={:}, shape={:}) with '{:}' (dtype={:}, shape={:}) | " + "Tolerance: [abs={:.5g}, rel={:.5g}] | Checking {:} error".format( + out0_name, + output0.dtype, + output0.shape, + out1_name, + output1.dtype, + output1.shape, + per_out_atol, + per_out_rtol, + per_out_err_stat, + ) + ) + G_LOGGER.extra_verbose( + "Note: Comparing {:} vs. {:}".format(iter_result0.runner_name, iter_result1.runner_name) + ) with G_LOGGER.indent(): if check_shapes and output0.shape != output1.shape: - G_LOGGER.error("Will not compare outputs of different shapes. Note: Output shapes are " - "{:} and {:}.".format(output0.shape, output1.shape)) - G_LOGGER.error("Note: Use --no-strict-shape-checking or set check_shapes=False to " - "attempt to compare values anyway.", mode=LogMode.ONCE) + G_LOGGER.error( + "Will not compare outputs of different shapes. Note: Output shapes are " + "{:} and {:}.".format(output0.shape, output1.shape) + ) + G_LOGGER.error( + "Note: Use --no-shape-check or set check_shapes=False to " + "attempt to compare values anyway.", + mode=LogMode.ONCE, + ) outputs_match = False else: output1 = util.try_match_shape(output1, output0.shape) output0 = output0.reshape(output1.shape) - outputs_match = check_outputs_match(output0, out0_name, output1, out1_name, - per_out_rtol=per_out_rtol, per_out_atol=per_out_atol, - per_out_err_stat=per_out_err_stat) + outputs_match = check_outputs_match( + output0, + out0_name, + output1, + out1_name, + per_out_rtol=per_out_rtol, + per_out_atol=per_out_atol, + per_out_err_stat=per_out_err_stat, + ) output_status[out0_name] = outputs_match if fail_fast and not outputs_match: return output_status - mismatched_output_names = [name for name, matched in output_status.items() if not matched] if mismatched_output_names: G_LOGGER.error("FAILED | Mismatched outputs: {:}".format(mismatched_output_names)) @@ -371,8 +437,10 @@ class CompareFunc(object): r0_outs = list(iter_result0.keys()) r1_name = iter_result1.runner_name r1_outs = list(iter_result1.keys()) - G_LOGGER.critical("All outputs were skipped, no common outputs found! Note:\n{:} outputs: " - "{:}\n{:} outputs: {:}".format(r0_name, r0_outs, r1_name, r1_outs)) + G_LOGGER.critical( + "All outputs were skipped, no common outputs found! Note:\n{:} outputs: " + "{:}\n{:} outputs: {:}".format(r0_name, r0_outs, r1_name, r1_outs) + ) return output_status diff --git a/tools/Polygraphy/polygraphy/comparator/data_loader.py b/tools/Polygraphy/polygraphy/comparator/data_loader.py index f53b7215..1c5423c5 100644 --- a/tools/Polygraphy/polygraphy/comparator/data_loader.py +++ b/tools/Polygraphy/polygraphy/comparator/data_loader.py @@ -22,13 +22,16 @@ from polygraphy.logger import G_LOGGER, LogMode np = mod.lazy_import("numpy") + @mod.export() class DataLoader(object): """ Generates synthetic input data. """ - def __init__(self, seed=None, iterations=None, input_metadata=None, - int_range=None, float_range=None, val_range=None): + + def __init__( + self, seed=None, iterations=None, input_metadata=None, int_range=None, float_range=None, val_range=None + ): """ Args: seed (int): @@ -45,6 +48,15 @@ class DataLoader(object): For input shape tensors, i.e. inputs whose *value* describes a shape in the model, the provided shape will be used to populate the values of the inputs, rather than to determine their shape. + val_range (Union[Tuple[number], Dict[str, Tuple[number]]]): + A tuple containing exactly 2 numbers, indicating the minimum and maximum values (inclusive) + the data loader should generate. + If either value in the tuple is None, the default will be used for that value. + If None is provided instead of a tuple, then the default values will be used for both the + minimum and maximum. + This can be specified on a per-input basis using a dictionary. In that case, + use an empty string ("") as the key to specify default range for inputs not explicitly listed. + int_range (Tuple[int]): [DEPRECATED - Use val_range instead] A tuple containing exactly 2 integers, indicating the minimum and maximum integer values (inclusive) @@ -59,15 +71,8 @@ class DataLoader(object): for that value. If None is provided instead of a tuple, then the default values will be used for both the minimum and maximum. - val_range (Union[Tuple[number], Dict[str, Tuple[number]]]): - A tuple containing exactly 2 numbers, indicating the minimum and maximum values (inclusive) - the data loader should generate. - If either value in the tuple is None, the default will be used for that value. - If None is provided instead of a tuple, then the default values will be used for both the - minimum and maximum. - This can be specified on a per-input basis using a dictionary. In that case, - use an empty string ("") as the key to specify default range for inputs not explicitly listed. """ + def default_tuple(tup, default): if tup is None or (not isinstance(tup, tuple) and not isinstance(tup, list)): return default @@ -95,14 +100,22 @@ class DataLoader(object): self.val_range = util.default(val_range, self.default_val_range) if self.user_input_metadata: - G_LOGGER.info("Will generate inference input data according to provided TensorMetadata: {}".format(self.user_input_metadata)) - + G_LOGGER.info( + "Will generate inference input data according to provided TensorMetadata: {}".format( + self.user_input_metadata + ) + ) def __repr__(self): - return util.make_repr("DataLoader", seed=self.seed, iterations=self.iterations, - input_metadata=self.user_input_metadata or None, int_range=self.int_range, - float_range=self.float_range, val_range=self.val_range)[0] - + return util.make_repr( + "DataLoader", + seed=self.seed, + iterations=self.iterations, + input_metadata=self.user_input_metadata or None, + int_range=self.int_range, + float_range=self.float_range, + val_range=self.val_range, + )[0] def _get_range(self, name, cast_type): if cast_type == int and self.int_range_set: @@ -121,10 +134,9 @@ class DataLoader(object): tup = self.default_val_range return tuple(cast_type(val) for val in tup) - def __getitem__(self, index): """ - Randomly generates input data. + Generates random input data. May update the DataLoader's `input_metadata` attribute. @@ -142,20 +154,25 @@ class DataLoader(object): G_LOGGER.verbose("Generating data using numpy seed: {:}".format(self.seed + index)) rng = np.random.RandomState(self.seed + index) - def get_static_shape(name, shape): static_shape = shape if util.is_shape_dynamic(shape): static_shape = util.override_dynamic_shape(shape) - if static_shape != shape and name not in self.user_input_metadata: + if static_shape != shape: if not util.is_valid_shape_override(static_shape, shape): - G_LOGGER.critical("Input tensor: {:} | Cannot override original shape: {:} to {:}".format(name, shape, static_shape)) - G_LOGGER.warning("Input tensor: {:} | Will generate data of shape: {:}.\n" - "If this is incorrect, please set input_metadata " - "or provide a custom data loader.".format(name, static_shape), mode=LogMode.ONCE) + G_LOGGER.critical( + "Input tensor: {:} | Cannot override original shape: {:} to {:}".format( + name, shape, static_shape + ) + ) + G_LOGGER.warning( + "Input tensor: {:} | Will generate data of shape: {:}.\n" + "If this is incorrect, please set input_metadata " + "or provide a custom data loader.".format(name, static_shape), + mode=LogMode.ONCE, + ) return static_shape - # Whether the user provided the values for a shape tensor input, # rather than the shape of the input. # If the shape is 1D, and has a value equal to the rank of the provided default shape, it is @@ -169,31 +186,36 @@ class DataLoader(object): user_shape = self.user_input_metadata[name].shape is_shape &= len(user_shape) == shape[0] - is_shape &= not util.is_shape_dynamic(user_shape) # Shape of shape cannot be dynamic. + is_shape &= not util.is_shape_dynamic(user_shape) # Shape of shape cannot be dynamic. return is_shape - def generate_buffer(name, dtype, shape): if is_shape_tensor(name, dtype): buffer = np.array(shape, dtype=dtype) - G_LOGGER.info("Assuming {:} is a shape tensor. Setting input values to: {:}. If this is not correct, " - "please set it correctly in 'input_metadata' or by providing --input-shapes".format(name, buffer), mode=LogMode.ONCE) + G_LOGGER.info( + "Assuming {:} is a shape tensor. Setting input values to: {:}. If this is not correct, " + "please set it correctly in 'input_metadata' or by providing --input-shapes".format(name, buffer), + mode=LogMode.ONCE, + ) elif np.issubdtype(dtype, np.integer) or np.issubdtype(dtype, np.bool_): imin, imax = self._get_range(name, cast_type=int if np.issubdtype(dtype, np.integer) else bool) - G_LOGGER.verbose("Input tensor: {:} | Generating input data in range: [{:}, {:}]".format(name, imin, imax), - mode=LogMode.ONCE) + G_LOGGER.verbose( + "Input tensor: {:} | Generating input data in range: [{:}, {:}]".format(name, imin, imax), + mode=LogMode.ONCE, + ) # high is 1 greater than the max int drawn. buffer = rng.randint(low=imin, high=imax + 1, size=shape, dtype=dtype) else: fmin, fmax = self._get_range(name, cast_type=float) - G_LOGGER.verbose("Input tensor: {:} | Generating input data in range: [{:}, {:}]".format(name, fmin, fmax), - mode=LogMode.ONCE) + G_LOGGER.verbose( + "Input tensor: {:} | Generating input data in range: [{:}, {:}]".format(name, fmin, fmax), + mode=LogMode.ONCE, + ) buffer = (rng.random_sample(size=shape) * (fmax - fmin) + fmin).astype(dtype) - buffer = np.array(buffer) # To handle scalars, since the above functions return a float if shape is (). + buffer = np.array(buffer) # To handle scalars, since the above functions return a float if shape is (). return buffer - if self.input_metadata is None and self.user_input_metadata is not None: self.input_metadata = self.user_input_metadata @@ -203,11 +225,21 @@ class DataLoader(object): user_dtype, user_shape = self.user_input_metadata[name] dtype = util.default(user_dtype, dtype) - is_valid_shape_override = user_shape is not None and util.is_valid_shape_override(user_shape, shape) - if not is_valid_shape_override and not is_shape_tensor(name, dtype): - G_LOGGER.warning("Input tensor: {:} | Cannot use provided custom shape: {:} " - "to override: {:}".format(name, user_shape, shape), mode=LogMode.ONCE) + + if util.is_shape_dynamic(user_shape): + G_LOGGER.warning( + "Input tensor: {:} | Provided input shape: {:} is dynamic.\n" + "Dynamic shapes cannot be used to generate inference data. " + "Will use default shape instead.\n" + "To avoid this, please provide a fixed shape to the data loader. ".format(name, user_shape) + ) + elif not is_valid_shape_override and not is_shape_tensor(name, dtype): + G_LOGGER.warning( + "Input tensor: {:} | Cannot use provided custom shape: {:} " + "to override: {:}. Will use default shape instead.".format(name, user_shape, shape), + mode=LogMode.ONCE, + ) else: shape = util.default(user_shape, shape) @@ -217,7 +249,9 @@ class DataLoader(object): # Warn about unused metadata for name in self.user_input_metadata.keys(): if name not in self.input_metadata: - msg = "Input tensor: {:} | Metadata was provided, but the input does not exist in one or more runners.".format(name) + msg = "Input tensor: {:} | Metadata was provided, but the input does not exist in one or more runners.".format( + name + ) close_match = util.find_in_dict(name, self.input_metadata) if close_match: msg += "\nMaybe you meant to set: {:}".format(close_match) @@ -225,8 +259,9 @@ class DataLoader(object): # Warn about unused val_range if not isinstance(self.val_range, tuple): - util.check_dict_contains(self.val_range, list(self.input_metadata.keys()) + [""], - check_missing=False, dict_name="val_range") + util.check_dict_contains( + self.val_range, list(self.input_metadata.keys()) + [""], check_missing=False, dict_name="val_range" + ) return buffers @@ -235,10 +270,9 @@ class DataLoader(object): class DataLoaderCache(object): def __init__(self, data_loader, save_inputs_path=None): self.data_loader = data_loader - self.cache = [] # List[OrderedDict[str, numpy.ndarray]] + self.cache = [] # List[OrderedDict[str, numpy.ndarray]] self.save_inputs_path = save_inputs_path - @func.constantmethod def __getitem__(self, iteration): """ @@ -254,17 +288,23 @@ class DataLoaderCache(object): def coerce_cached_input(index, name, dtype, shape): cached_feed_dict = self.cache[iteration] cached_name = util.find_in_dict(name, cached_feed_dict, index) - assert cached_name is not None + util.check(cached_name is not None) if cached_name != name: - G_LOGGER.warning("Input tensor: {:} | Cached buffer name ({:}) does not match input name ({:}).".format( - name, cached_name, name)) + G_LOGGER.warning( + "Input tensor: {:} | Buffer name ({:}) does not match expected input name ({:}).".format( + name, cached_name, name + ) + ) buffer = cached_feed_dict[cached_name] if dtype != buffer.dtype: - G_LOGGER.warning("Input tensor: {:} | Cached buffer dtype ({:}) does not match input dtype ({:}), attempting cast. ".format( - name, buffer.dtype, np.dtype(dtype).name)) + G_LOGGER.warning( + "Input tensor: {:} | Buffer dtype ({:}) does not match expected input dtype ({:}), attempting to cast. ".format( + name, buffer.dtype, np.dtype(dtype).name + ) + ) type_info = None if np.issubdtype(dtype, np.integer): @@ -273,18 +313,24 @@ class DataLoaderCache(object): type_info = np.finfo(np.dtype(dtype)) if type_info is not None and np.any((buffer < type_info.min) | (buffer > type_info.max)): - G_LOGGER.warning("Some values in this input arre out of range of {:}. Unexpected behavior may ensue!".format(dtype)) + G_LOGGER.warning( + "Some values in this input are out of range of {:}. Unexpected behavior may ensue!".format( + dtype + ) + ) buffer = buffer.astype(dtype) if not util.is_valid_shape_override(buffer.shape, shape): - G_LOGGER.warning("Input tensor: {:} | Cached buffer shape ({:}) does not match input shape ({:}), attempting reshape. ".format( - name, buffer.shape, shape)) + G_LOGGER.warning( + "Input tensor: {:} | Buffer shape ({:}) does not match expected input shape ({:}), attempting to transpose/reshape. ".format( + name, buffer.shape, shape + ) + ) buffer = util.try_match_shape(buffer, shape) - assert buffer.dtype == dtype and util.is_valid_shape_override(buffer.shape, shape) + util.check(buffer.dtype == dtype and util.is_valid_shape_override(buffer.shape, shape)) return buffer - feed_dict = OrderedDict() # Reload from data loader if needed @@ -294,21 +340,27 @@ class DataLoaderCache(object): try: buffer = coerce_cached_input(index, name, dtype, shape) except AssertionError: - G_LOGGER.warning("Could not reuse input: {:} across runners. Attempting to reload " - "inputs from the data loader. Note that this will only work if the data loader " - "supports random access.".format(name)) + G_LOGGER.warning( + "Could not use buffer previously cached from data loader for input: {:}. Attempting to reload " + "inputs from the data loader.\n" + "Note that this will only work if the data loader supports random access.\n" + "Please refer to warnings above for details on why the previously generated input buffer didn't work. ".format( + name + ) + ) try: if data_loader_feed_dict is None: data_loader_feed_dict = self.data_loader[iteration] buffer = data_loader_feed_dict[name] except: - G_LOGGER.critical("Could not reload inputs from data loader. Are the runners running the same model? " - "If not, please rewrite the data loader to support random access.") + G_LOGGER.critical( + "Could not reload inputs from data loader. Are the runners running the same model? " + "If not, please rewrite the data loader to support random access." + ) feed_dict[name] = buffer return feed_dict - def set_input_metadata(self, input_metadata): """ Set the input metadata for the data loader. diff --git a/tools/Polygraphy/polygraphy/comparator/postprocess.py b/tools/Polygraphy/polygraphy/comparator/postprocess.py index c4d4d223..d04fd5ea 100644 --- a/tools/Polygraphy/polygraphy/comparator/postprocess.py +++ b/tools/Polygraphy/polygraphy/comparator/postprocess.py @@ -17,6 +17,7 @@ from polygraphy import mod np = mod.lazy_import("numpy") + @mod.export() class PostprocessFunc(object): """ @@ -55,7 +56,6 @@ class PostprocessFunc(object): return k[""] return None - for name, output in iter_result.items(): k_val = get_k(name) if k_val: @@ -63,4 +63,5 @@ class PostprocessFunc(object): axis_len = indices.shape[axis] iter_result[name] = np.take(indices, np.arange(0, min(k_val, axis_len)), axis=axis) return iter_result + return topk diff --git a/tools/Polygraphy/polygraphy/comparator/struct.py b/tools/Polygraphy/polygraphy/comparator/struct.py index 3095639c..c1d77d03 100644 --- a/tools/Polygraphy/polygraphy/comparator/struct.py +++ b/tools/Polygraphy/polygraphy/comparator/struct.py @@ -19,8 +19,7 @@ from collections import OrderedDict from polygraphy import mod, util, config from polygraphy.common.interface import TypedDict, TypedList -from polygraphy.json import (Decoder, Encoder, add_json_methods, load_json, - save_json) +from polygraphy.json import Decoder, Encoder, add_json_methods, load_json, save_json from polygraphy.logger import G_LOGGER np = mod.lazy_import("numpy") @@ -42,13 +41,15 @@ class LazyNumpyArray(object): self.tmpfile = None if config.ARRAY_SWAP_THRESHOLD_MB >= 0 and arr.nbytes > (config.ARRAY_SWAP_THRESHOLD_MB << 20): self.tmpfile = tempfile.NamedTemporaryFile(mode="w+", suffix=".json") - G_LOGGER.extra_verbose("Evicting large array ({:.3f} MiB) from memory and saving to {:}".format( - arr.nbytes / (1024.0 ** 2), self.tmpfile.name)) + G_LOGGER.extra_verbose( + "Evicting large array ({:.3f} MiB) from memory and saving to {:}".format( + arr.nbytes / (1024.0 ** 2), self.tmpfile.name + ) + ) save_json(arr, self.tmpfile) else: self.arr = arr - def numpy(self): """ Get the NumPy array, deserializing from the disk if it was stored earlier. @@ -88,13 +89,13 @@ class IterationResult(TypedDict(lambda: str, lambda: LazyNumpyArray)): Also includes additional fields indicating the name of the runner which produced the outputs, and the time required to do so. """ + @staticmethod def _to_lazy(nparray): if isinstance(nparray, LazyNumpyArray): return nparray return LazyNumpyArray(nparray) - @staticmethod def _to_lazy_dict(nparray_dict): if nparray_dict is None: @@ -106,7 +107,6 @@ class IterationResult(TypedDict(lambda: str, lambda: LazyNumpyArray)): lazy[name] = IterationResult._to_lazy(out) return lazy - def __init__(self, outputs=None, runtime=None, runner_name=None): """ Args: @@ -119,38 +119,34 @@ class IterationResult(TypedDict(lambda: str, lambda: LazyNumpyArray)): if outputs and config.ARRAY_SWAP_THRESHOLD_MB < 0: total_size_gb = sum(arr.nbytes for arr in outputs.values() if isinstance(arr, np.ndarray)) / (1024.0 ** 3) if total_size_gb >= 1: - G_LOGGER.warning("It looks like the outputs of this network are very large ({:.3f} GiB).\n" - "To reduce memory usage, you may want to allow Polygraphy to swap these arrays to the disk using " - "the POLYGRAPHY_ARRAY_SWAP_THRESHOLD_MB environment variable.".format(total_size_gb)) + G_LOGGER.warning( + "It looks like the outputs of this network are very large ({:.3f} GiB).\n" + "To reduce memory usage, you may want to allow Polygraphy to swap these arrays to the disk using " + "the POLYGRAPHY_ARRAY_SWAP_THRESHOLD_MB environment variable.".format(total_size_gb) + ) super().__init__(IterationResult._to_lazy_dict(outputs)) self.runtime = runtime self.runner_name = util.default(runner_name, "") - # Convenience methods to preserve np.ndarray in the interface. def update(self, other): return super().update(IterationResult._to_lazy_dict(other)) - def __setitem__(self, name, arr): return super().__setitem__(name, IterationResult._to_lazy(arr)) - def values(self): for arr in super().values(): yield arr.numpy() - def items(self): for name, arr in super().items(): yield name, arr.numpy() - def __getitem__(self, name): return super().__getitem__(name).numpy() - def __eq__(self, other): if self.runtime != other.runtime or self.runner_name != other.runner_name: return False @@ -188,6 +184,7 @@ class RunResults(TypedList(lambda: tuple)): Note: Technically, this is a ``List[Tuple[str, List[IterationResult]]]``, but includes helpers that make it behave like an OrderedDict that can contain duplicates. """ + def items(self): """ Creates a generator that yields ``Tuple[str, List[IterationResult]]`` - runner names @@ -196,7 +193,6 @@ class RunResults(TypedList(lambda: tuple)): for name, iteration_results in self.lst: yield name, iteration_results - def keys(self): """ Creates a generator that yields runner names (str). @@ -204,7 +200,6 @@ class RunResults(TypedList(lambda: tuple)): for name, _ in self.lst: yield name - def values(self): """ Creates a generator that yields runner outputs (List[IterationResult]). @@ -212,7 +207,6 @@ class RunResults(TypedList(lambda: tuple)): for _, iteration_results in self.lst: yield iteration_results - def update(self, other): """ Updates the results stored in this instance. @@ -225,7 +219,6 @@ class RunResults(TypedList(lambda: tuple)): self.lst[name] = iteration_results return self - def __getitem__(self, key): if isinstance(key, int): return self.lst[key] @@ -234,9 +227,11 @@ class RunResults(TypedList(lambda: tuple)): if name == key: return iteration_results - G_LOGGER.critical("{:35} does not exist in this RunResults instance. Note: Available runners: {:}".format( - key, list(self.keys()))) - + G_LOGGER.critical( + "{:35} does not exist in this RunResults instance. Note: Available runners: {:}".format( + key, list(self.keys()) + ) + ) def __setitem__(self, key, value): if isinstance(key, int): @@ -250,13 +245,11 @@ class RunResults(TypedList(lambda: tuple)): else: self.append((key, value)) - def __contains__(self, val): if isinstance(val, str) or isinstance(val, bytes): return val in list(self.keys()) return val in self.lst - def __eq__(self, other): for (r0, its0), (r1, its1) in zip(self.lst, other.lst): if r0 != r1: @@ -304,6 +297,7 @@ class AccuracyResult(TypedDict(lambda: tuple, lambda: list)): runner0_output = run_results["runner0"][iteration][output_name] runner1_output = run_results["runner1"][iteration][output_name] """ + def __bool__(self): """ Whether all outputs matched for every iteration. @@ -318,11 +312,9 @@ class AccuracyResult(TypedDict(lambda: tuple, lambda: list)): """ return all([bool(match) for outs in self.values() for out in outs for match in out.values()]) - def _get_runner_pair(self, runner_pair): return util.default(runner_pair, list(self.keys())[0]) - def percentage(self, runner_pair=None): """ Returns the percentage of iterations that matched for the given pair of runners, @@ -336,14 +328,13 @@ class AccuracyResult(TypedDict(lambda: tuple, lambda: list)): Defaults to the first pair in the dictionary. """ if not list(self.keys()): - return 1.0 # No data in this result. + return 1.0 # No data in this result. matched, _, total = self.stats(runner_pair) if not total: - return 1.0 # No iterations + return 1.0 # No iterations return float(matched) / float(total) - def stats(self, runner_pair=None): """ Returns the number of iterations that matched, mismatched, and the total number of iterations. diff --git a/tools/Polygraphy/polygraphy/comparator/util.py b/tools/Polygraphy/polygraphy/comparator/util.py index 414ae2ef..005bba21 100644 --- a/tools/Polygraphy/polygraphy/comparator/util.py +++ b/tools/Polygraphy/polygraphy/comparator/util.py @@ -12,6 +12,7 @@ def zero_on_empty(func): if util.is_empty_shape(buffer.shape): return 0 return func(buffer) + return wrapped @@ -69,7 +70,7 @@ def str_histogram(output, hist_range=None): return "" max_num_elems = compute_max(hist) - if not max_num_elems: # Empty tensor + if not max_num_elems: # Empty tensor return bin_edges = ["{:.3g}".format(bin) for bin in bin_edges] @@ -78,12 +79,19 @@ def str_histogram(output, hist_range=None): MAX_WIDTH = 40 ret = "---- Histogram ----\n" - ret += "{:{width}}| Num Elems | Visualization\n".format("Bin Range", width=max_start_bin_width + max_end_bin_width + 5) + ret += "{:{width}}| Num Elems | Visualization\n".format( + "Bin Range", width=max_start_bin_width + max_end_bin_width + 5 + ) for num, bin_start, bin_end in zip(hist, bin_edges, bin_edges[1:]): bar = "#" * int(MAX_WIDTH * float(num) / float(max_num_elems)) ret += "({:<{max_start_bin_width}}, {:<{max_end_bin_width}}) | {:10} | {:}\n".format( - bin_start, bin_end, num, bar, - max_start_bin_width=max_start_bin_width, max_end_bin_width=max_end_bin_width) + bin_start, + bin_end, + num, + bar, + max_start_bin_width=max_start_bin_width, + max_end_bin_width=max_end_bin_width, + ) return ret except Exception as err: G_LOGGER.verbose("Could not generate histogram.\nNote: Error was: {:}".format(err)) @@ -100,10 +108,18 @@ def str_output_stats(output, runner_name=None): try: with np.testing.suppress_warnings() as sup: sup.filter(RuntimeWarning) - ret += "mean={:.5g}, std-dev={:.5g}, var={:.5g}, median={:.5g}, min={:.5g} at {:}, max={:.5g} at {:}\n".format( - compute_mean(output), compute_stddev(output), compute_variance(output), compute_median(output), - compute_min(output), compute_argmin(output), - compute_max(output), compute_argmax(output)) + ret += ( + "mean={:.5g}, std-dev={:.5g}, var={:.5g}, median={:.5g}, min={:.5g} at {:}, max={:.5g} at {:}\n".format( + compute_mean(output), + compute_stddev(output), + compute_variance(output), + compute_median(output), + compute_min(output), + compute_argmin(output), + compute_max(output), + compute_argmax(output), + ) + ) except Exception as err: G_LOGGER.verbose("Could not generate statistics.\nNote: Error was: {:}".format(err)) ret += "" @@ -117,4 +133,6 @@ def log_output_stats(output, info_hist=False, runner_name=None, hist_range=None) G_LOGGER.info(ret) with G_LOGGER.indent(): # Show histogram on failures. - G_LOGGER.log(lambda: str_histogram(output, hist_range), severity=G_LOGGER.INFO if info_hist else G_LOGGER.VERBOSE) + G_LOGGER.log( + lambda: str_histogram(output, hist_range), severity=G_LOGGER.INFO if info_hist else G_LOGGER.VERBOSE + ) diff --git a/tools/Polygraphy/polygraphy/config.py b/tools/Polygraphy/polygraphy/config.py index 1c2b2eac..f720915b 100644 --- a/tools/Polygraphy/polygraphy/config.py +++ b/tools/Polygraphy/polygraphy/config.py @@ -14,7 +14,7 @@ # limitations under the License. # import os - +import sys INTERNAL_CORRECTNESS_CHECKS = bool(os.environ.get("POLYGRAPHY_INTERNAL_CORRECTNESS_CHECKS", "0") != "0") """ @@ -28,6 +28,14 @@ Whether Polygraphy will automatically install required Python packages at runtim This can be configured by setting the 'POLYGRAPHY_AUTOINSTALL_DEPS' environment variable. """ +INSTALL_CMD = os.environ.get("POLYGRAPHY_INSTALL_CMD", "{:} -m pip install".format(sys.executable)).split() +""" +The command to use to automatically install dependencies. Only relevant when AUTOINSTALL_DEPS +is enabled. Defaults to ``["python3", "-m", "pip", "install"]``. +This can be configured by setting the 'POLYGRAPHY_INSTALL_CMD' environment variable to a +string containing the command; for example: ``python3 -m pip install``. +""" + ARRAY_SWAP_THRESHOLD_MB = int(os.environ.get("POLYGRAPHY_ARRAY_SWAP_THRESHOLD_MB", "-1")) """ The threshold, in megabytes, above which Polygraphy will evict a NumPy array from memory and swap it to disk. diff --git a/tools/Polygraphy/polygraphy/constants.py b/tools/Polygraphy/polygraphy/constants.py index ae8d5987..3aaed30d 100644 --- a/tools/Polygraphy/polygraphy/constants.py +++ b/tools/Polygraphy/polygraphy/constants.py @@ -20,11 +20,12 @@ from polygraphy.config import AUTOINSTALL_DEPS, INTERNAL_CORRECTNESS_CHECKS DEFAULT_SHAPE_VALUE = 1 DEFAULT_SEED = 1 -TAB = " " * 4 # The one true tab +TAB = " " * 4 # The one true tab MARK_ALL = "mark-all" """ Special value for ModifyOutputs loaders indicating that all values should be marked as outputs """ -TYPE_MARKER = "polygraphy_serialized_json_type" +LEGACY_TYPE_MARKER = "polygraphy_serialized_json_type" +TYPE_MARKER = "polygraphy_class" diff --git a/tools/Polygraphy/polygraphy/cuda/cuda.py b/tools/Polygraphy/polygraphy/cuda/cuda.py index 008f0864..ecf555c4 100644 --- a/tools/Polygraphy/polygraphy/cuda/cuda.py +++ b/tools/Polygraphy/polygraphy/cuda/cuda.py @@ -30,6 +30,7 @@ class MemcpyKind(object): """ Enumerates different kinds of copy operations. """ + HostToHost = ctypes.c_int(0) """Copies from host memory to host memory""" HostToDevice = ctypes.c_int(1) @@ -49,18 +50,21 @@ class Cuda(object): Wrapper that exposes low-level CUDA functionality. """ + def __init__(self): self.handle = ctypes.CDLL("libcudart.so") if not self.handle: G_LOGGER.critical("Could not load the CUDA runtime library. Is it on your loader path?") - @func.constantmethod def check(self, status): if status != 0: - G_LOGGER.critical("CUDA Error: {:}. To figure out what this means, refer to " - "https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__TYPES.html#group__CUDART__TYPES_1g3f51e3575c2178246db0a94a430e0038".format(status)) - + G_LOGGER.critical( + "CUDA Error: {:}. To figure out what this means, refer to " + "https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__TYPES.html#group__CUDART__TYPES_1g3f51e3575c2178246db0a94a430e0038".format( + status + ) + ) @func.constantmethod def create_stream(self): @@ -69,19 +73,16 @@ class Cuda(object): self.check(self.handle.cudaStreamCreate(ctypes.byref(ptr))) return ptr.value - @func.constantmethod def stream_synchronize(self, ptr): # Signature: int -> None self.check(self.handle.cudaStreamSynchronize(void_ptr(ptr))) - @func.constantmethod def destroy_stream(self, ptr): # Signature: int -> None self.check(self.handle.cudaStreamDestroy(void_ptr(ptr))) - @func.constantmethod def malloc(self, nbytes): """ @@ -97,11 +98,10 @@ class Cuda(object): PolygraphyException: If an error was encountered during the allocation. """ ptr = void_ptr() - nbytes = ctypes.c_size_t(nbytes) # Required to prevent overflow + nbytes = ctypes.c_size_t(nbytes) # Required to prevent overflow self.check(self.handle.cudaMalloc(ctypes.byref(ptr), nbytes)) return ptr.value - @func.constantmethod def free(self, ptr): """ @@ -115,7 +115,6 @@ class Cuda(object): """ self.check(self.handle.cudaFree(void_ptr(ptr))) - @func.constantmethod def memcpy(self, dst, src, nbytes, kind, stream_ptr=None): """ @@ -137,7 +136,7 @@ class Cuda(object): Raises: PolygraphyException: If an error was encountered during the copy. """ - nbytes = ctypes.c_size_t(nbytes) # Required to prevent overflow + nbytes = ctypes.c_size_t(nbytes) # Required to prevent overflow if stream_ptr is not None: self.check(self.handle.cudaMemcpyAsync(void_ptr(dst), void_ptr(src), nbytes, kind, void_ptr(stream_ptr))) else: @@ -145,6 +144,8 @@ class Cuda(object): G_CUDA = None + + @mod.export() def wrapper(): """ @@ -154,7 +155,8 @@ def wrapper(): Cuda: The global CUDA wrapper. """ global G_CUDA - G_CUDA = util.default(G_CUDA, Cuda()) + if G_CUDA is None: + G_CUDA = Cuda() return G_CUDA @@ -163,22 +165,20 @@ class Stream(object): """ High-level wrapper for a CUDA stream. """ + def __init__(self): self.ptr = wrapper().create_stream() """int: The memory address of the underlying CUDA stream""" - def __enter__(self): return self - def __exit__(self, exc_type, exc_value, traceback): """ Frees the underlying CUDA stream. """ self.free() - def free(self): """ Frees the underlying CUDA stream. @@ -193,7 +193,6 @@ class Stream(object): wrapper().destroy_stream(self.ptr) self.handle = ctypes.c_void_p(None) - def synchronize(self): """ Synchronizes the stream. @@ -212,6 +211,7 @@ class DeviceView(object): """ A read-only view of a GPU memory region. """ + def __init__(self, ptr, shape, dtype): """ Args: @@ -227,12 +227,12 @@ class DeviceView(object): self.dtype = dtype """np.dtype: The data type of the device buffer""" - def _check_dtype_matches(self, host_buffer): if host_buffer.dtype != self.dtype: - G_LOGGER.error("Host buffer type: {:} does not match the type of this device buffer: {:}. " - "This may cause CUDA errors!".format(host_buffer.dtype, self.dtype)) - + G_LOGGER.error( + "Host buffer type: {:} does not match the type of this device buffer: {:}. " + "This may cause CUDA errors!".format(host_buffer.dtype, self.dtype) + ) @property def nbytes(self): @@ -241,7 +241,6 @@ class DeviceView(object): """ return util.volume(self.shape) * np.dtype(self.dtype).itemsize - @func.constantmethod def copy_to(self, host_buffer, stream=None): """ @@ -266,22 +265,28 @@ class DeviceView(object): try: host_buffer.resize(self.shape, refcheck=False) except ValueError as err: - G_LOGGER.warning("Could not resize host buffer to shape: {:}. Allocating a new buffer instead.\n" - "Note: Error was: {:}".format(self.shape, err)) + G_LOGGER.warning( + "Could not resize host buffer to shape: {:}. Allocating a new buffer instead.\n" + "Note: Error was: {:}".format(self.shape, err) + ) host_buffer = np.empty(self.shape, dtype=np.dtype(self.dtype)) if not self.nbytes: return host_buffer host_buffer = np.ascontiguousarray(host_buffer) - wrapper().memcpy(dst=host_buffer.ctypes.data, src=self.ptr, nbytes=self.nbytes, - kind=MemcpyKind.DeviceToHost, stream_ptr=try_get_stream_handle(stream)) + wrapper().memcpy( + dst=host_buffer.ctypes.data, + src=self.ptr, + nbytes=self.nbytes, + kind=MemcpyKind.DeviceToHost, + stream_ptr=try_get_stream_handle(stream), + ) # Use resize instead of reshape since it operates in-place. host_buffer.resize(self.shape, refcheck=False) return host_buffer - @func.constantmethod def numpy(self): """ @@ -293,9 +298,10 @@ class DeviceView(object): arr = np.empty(self.shape, dtype=self.dtype) return self.copy_to(arr) - def __str__(self): - return "DeviceView[(dtype={:}, shape={:}), ptr={:}]".format(np.dtype(self.dtype).name, self.shape, hex(self.ptr)) + return "DeviceView[(dtype={:}, shape={:}), ptr={:}]".format( + np.dtype(self.dtype).name, self.shape, hex(self.ptr) + ) @mod.export() @@ -303,6 +309,7 @@ class DeviceArray(DeviceView): """ An array on the GPU. """ + def __init__(self, shape=None, dtype=None): """ Args: @@ -313,24 +320,20 @@ class DeviceArray(DeviceView): self.allocated_nbytes = 0 self.resize(self.shape) - def __enter__(self): return self - def __exit__(self, exc_type, exc_value, traceback): """ Frees the underlying memory of this DeviceArray. """ self.free() - def allocate(self, nbytes): if nbytes: self.ptr = wrapper().malloc(nbytes) self.allocated_nbytes = nbytes - def free(self): """ Frees the GPU memory associated with this array. @@ -346,7 +349,6 @@ class DeviceArray(DeviceView): self.allocated_nbytes = 0 self.ptr = 0 - def resize(self, shape): """ Resizes or reshapes the array to the specified shape. @@ -363,7 +365,6 @@ class DeviceArray(DeviceView): self.allocate(nbytes) self.shape = shape - def copy_from(self, host_buffer, stream=None): """ Copies from the provided host buffer into this device buffer. @@ -384,10 +385,16 @@ class DeviceArray(DeviceView): self._check_dtype_matches(host_buffer) self.resize(host_buffer.shape) host_buffer = np.ascontiguousarray(host_buffer.ravel()) - wrapper().memcpy(dst=self.ptr, src=host_buffer.ctypes.data, nbytes=host_buffer.nbytes, - kind=MemcpyKind.HostToDevice, stream_ptr=try_get_stream_handle(stream)) + wrapper().memcpy( + dst=self.ptr, + src=host_buffer.ctypes.data, + nbytes=host_buffer.nbytes, + kind=MemcpyKind.HostToDevice, + stream_ptr=try_get_stream_handle(stream), + ) return self - def __str__(self): - return "DeviceArray[(dtype={:}, shape={:}), ptr={:}]".format(np.dtype(self.dtype).name, self.shape, hex(self.ptr)) + return "DeviceArray[(dtype={:}, shape={:}), ptr={:}]".format( + np.dtype(self.dtype).name, self.shape, hex(self.ptr) + ) diff --git a/tools/Polygraphy/polygraphy/exception/exception.py b/tools/Polygraphy/polygraphy/exception/exception.py index 68b5698f..51fbd811 100644 --- a/tools/Polygraphy/polygraphy/exception/exception.py +++ b/tools/Polygraphy/polygraphy/exception/exception.py @@ -17,14 +17,17 @@ from polygraphy import mod +# Do not raise this exception manually. Instead, use G_LOGGER.critical(). @mod.export() class PolygraphyException(Exception): """ An exception raised by Polygraphy. """ + pass +# Do not raise this exception manually. Instead, use G_LOGGER.internal_error(). @mod.export() class PolygraphyInternalException(Exception): """ @@ -34,4 +37,5 @@ class PolygraphyInternalException(Exception): This is *not* a child class of PolygraphyException because it indicates a bug in Polygraphy itself. """ + pass diff --git a/tools/Polygraphy/polygraphy/func/func.py b/tools/Polygraphy/polygraphy/func/func.py index 7927b85a..0e35b47f 100644 --- a/tools/Polygraphy/polygraphy/func/func.py +++ b/tools/Polygraphy/polygraphy/func/func.py @@ -22,7 +22,7 @@ from polygraphy.logger import G_LOGGER def make_iterable(obj): - return obj if type(obj) == tuple else (obj, ) + return obj if type(obj) == tuple else (obj,) @mod.export() @@ -79,6 +79,7 @@ def extend(extend_func): Args: extend_func (Callable): A callable to extend. """ + def extend_decorator(func): @functools.wraps(func) def extended_func(*args, **kwargs): @@ -92,22 +93,30 @@ def extend(extend_func): elif len(extend_func_ret_tuple) == len(func_args): func_retval = func(*extend_func_ret_tuple) else: + def try_get_name(fn): try: return fn.__name__ except: return fn - G_LOGGER.critical("Function: {:} accepts {:} parameter(s), but " - "needs to accept {:} parameter(s) from: {:} instead.\nNote: Parameters should be: {:}".format( - try_get_name(func), len(func_args), len(extend_func_ret_tuple), - try_get_name(extend_func), tuple(map(type, extend_func_ret_tuple)))) + G_LOGGER.critical( + "Function: {:} accepts {:} parameter(s), but " + "needs to accept {:} parameter(s) from: {:} instead.\nNote: Parameters should be: {:}".format( + try_get_name(func), + len(func_args), + len(extend_func_ret_tuple), + try_get_name(extend_func), + tuple(map(type, extend_func_ret_tuple)), + ) + ) if func_retval is not None: return func_retval return extend_func_retval return extended_func + return extend_decorator @@ -148,7 +157,11 @@ def constantmethod(func): ret = func(self, *args, **kwargs) finally: if vars(self) != old_dict: - G_LOGGER.internal_error("{:} was mutated in a constant method! Note:\nOld state: {:}\nNew state: {:}".format(self, old_dict, vars(self))) + G_LOGGER.internal_error( + "{:} was mutated in a constant method! Note:\nOld state: {:}\nNew state: {:}".format( + self, old_dict, vars(self) + ) + ) return ret return wrapper diff --git a/tools/Polygraphy/polygraphy/json/serde.py b/tools/Polygraphy/polygraphy/json/serde.py index 07bc99e6..f781897d 100644 --- a/tools/Polygraphy/polygraphy/json/serde.py +++ b/tools/Polygraphy/polygraphy/json/serde.py @@ -14,6 +14,7 @@ # limitations under the License. # +import base64 import functools import io import json @@ -27,14 +28,20 @@ util = mod.lazy_import("polygraphy.util.util") TYPE_STRING_PREFIX = "__polygraphy_encoded_" -def str_from_type(typ): + +def legacy_str_from_type(typ): return TYPE_STRING_PREFIX + typ.__name__ +def str_from_type(typ): + return typ.__name__ + + class BaseCustomImpl(object): """ Base class for Polygraphy's JSON encoder/decoder. """ + @classmethod def register(cls, typ): """ @@ -93,33 +100,44 @@ class BaseCustomImpl(object): Args: typ (type): The type of the class for which to register the function. """ + def register_impl(func): def add(key, val): if key in cls.polygraphy_registered: - G_LOGGER.critical("Duplicate serialization function for type: {:}.\n" - "Note: Existing function: {:}, New function: {:}".format( - key, cls.polygraphy_registered[key], func)) + G_LOGGER.critical( + "Duplicate serialization function for type: {:}.\n" + "Note: Existing function: {:}, New function: {:}".format( + key, cls.polygraphy_registered[key], func + ) + ) cls.polygraphy_registered[key] = val - if cls == Encoder: + def wrapped(obj): dct = func(obj) - dct[str_from_type(typ)] = constants.TYPE_MARKER + dct[constants.TYPE_MARKER] = str_from_type(typ) return dct add(typ, wrapped) return wrapped elif cls == Decoder: + def wrapped(dct): - del dct[str_from_type(typ)] + if constants.TYPE_MARKER in dct: + del dct[constants.TYPE_MARKER] + + type_name = legacy_str_from_type(typ) + if type_name in dct: + del dct[type_name] + return func(dct) + add(legacy_str_from_type(typ), wrapped) add(str_from_type(typ), wrapped) else: G_LOGGER.critical("Cannot register for unrecognized class type: ") - return register_impl @@ -128,6 +146,7 @@ class Encoder(BaseCustomImpl, json.JSONEncoder): """ Polygraphy's custom JSON Encoder implementation. """ + polygraphy_registered = {} def default(self, o): @@ -141,6 +160,7 @@ class Decoder(BaseCustomImpl): """ Polygraphy's custom JSON Decoder implementation. """ + polygraphy_registered = {} def __call__(self, pairs): @@ -149,19 +169,27 @@ class Decoder(BaseCustomImpl): if config.INTERNAL_CORRECTNESS_CHECKS: custom_type_keys = [key for key in dct if key.startswith(TYPE_STRING_PREFIX)] if custom_type_keys and custom_type_keys[0] not in self.polygraphy_registered: - G_LOGGER.internal_error("Custom type has no decode function registered! " - "Note: Encoded object is:\n{:}".format(dct)) + G_LOGGER.internal_error( + "Custom type has no decode function registered! " "Note: Encoded object is:\n{:}".format(dct) + ) # The encoder will insert special key-value pairs into dictionaries encoded from # custom types. If we find one, then we know to decode using the corresponding custom # type function. + type_name = dct.get(constants.TYPE_MARKER) + func = self.polygraphy_registered.get(type_name) + if func: + return func(dct) + for type_str, func in self.polygraphy_registered.items(): - if type_str in dct and dct[type_str] == constants.TYPE_MARKER: # Found a custom type! + if type_str in dct and dct[type_str] == constants.LEGACY_TYPE_MARKER: # Found a custom type! return func(dct) return dct NUMPY_REGISTRATION_SUCCESS = False + + def try_register_numpy_json(func): """ Decorator that attempts to register JSON encode/decode methods @@ -170,31 +198,44 @@ def try_register_numpy_json(func): This needs to be attempted multiple times because numpy may become available in the middle of execution - for example, if using dependency auto-installation. """ + @functools.wraps(func) def wrapped(*args, **kwargs): global NUMPY_REGISTRATION_SUCCESS if not NUMPY_REGISTRATION_SUCCESS and mod.has_mod(np, "__version__"): - # We define this along-side load_json/save_json so that it is guaranteed to be + # We define this alongside load_json/save_json so that it is guaranteed to be # imported before we need to encode/decode NumPy arrays. @Encoder.register(np.ndarray) def encode(array): outfile = io.BytesIO() - np.savez(outfile, array) + np.save(outfile, array, allow_pickle=False) outfile.seek(0) - return { - "array": outfile.read().decode('latin-1') - } - + data = base64.b64encode(outfile.read()).decode() + return {"array": data} @Decoder.register(np.ndarray) def decode(dct): - infile = io.BytesIO(dct["array"].encode('latin-1')) - # We always encode arrays separately. - return list(np.load(infile, allow_pickle=False).values())[0] + def load(mode="base64"): + if mode == "base64": + data = base64.b64decode(dct["array"].encode(), validate=True) + elif mode == "latin-1": + data = dct["array"].encode(mode) + else: + assert False, "Unsupported mode: {:}".format(mode) + infile = io.BytesIO(data) + return np.load(infile, allow_pickle=False) + try: + arr = load() + except: + arr = load("latin-1") # For backwards compatibility + if isinstance(arr, np.ndarray): + return arr + return list(arr.values())[0] # For backwards compatibility NUMPY_REGISTRATION_SUCCESS = True return func(*args, **kwargs) + return wrapped @@ -230,9 +271,13 @@ def from_json(src): return json.loads(src, object_pairs_hook=Decoder()) -@mod.export_deprecated_alias("pickle_save", remove_in="0.31.0", use_instead="JSON serialization. " - "This function has been migrated to use JSON and will NOT pickle the input object. " - "Use save_json") +@mod.export_deprecated_alias( + "pickle_save", + remove_in="0.31.0", + use_instead="JSON serialization. " + "This function has been migrated to use JSON and will NOT pickle the input object. " + "Use save_json", +) @mod.export() @try_register_numpy_json def save_json(obj, dest, description=None): @@ -268,10 +313,12 @@ def load_json(src, description=None): except UnicodeDecodeError: # This is a pickle file from Polygraphy 0.26.1 or older. mod.warn_deprecated("pickle", use_instead="JSON", remove_in="0.31.0") - G_LOGGER.critical("It looks like you're trying to load a Pickle file.\nPolygraphy migrated to using JSON " - "instead of Pickle in version 0.27.0 for security reasons.\nYou can convert your existing " - "pickled data to JSON using the command-line tool: `polygraphy to-json {:} -o new.json`.\nAll data serialized " - "from this and future versions of Polygraphy will always use JSON. ".format(src)) + G_LOGGER.critical( + "It looks like you're trying to load a Pickle file.\nPolygraphy migrated to using JSON " + "instead of Pickle in version 0.27.0 for security reasons.\nYou can convert your existing " + "pickled data to JSON using the command-line tool: `polygraphy to-json {:} -o new.json`.\nAll data serialized " + "from this and future versions of Polygraphy will always use JSON. ".format(src) + ) @mod.export() @@ -288,16 +335,18 @@ def add_json_methods(description=None): description (str): A description of what is being saved or loaded. """ + def add_json_methods_impl(cls): # JSON methods def check_decoded(obj): if not isinstance(obj, cls): - G_LOGGER.critical("Provided JSON cannot be decoded into a {:}.\n" - "Note: JSON was decoded into a {:}:\n{:}".format(cls.__name__, type(obj), obj)) + G_LOGGER.critical( + "Provided JSON cannot be decoded into a {:}.\n" + "Note: JSON was decoded into a {:}:\n{:}".format(cls.__name__, type(obj), obj) + ) return obj - def _to_json_method(self): """ Encode this instance as a JSON object. @@ -307,11 +356,9 @@ def add_json_methods(description=None): """ return to_json(self) - def _from_json_method(src): return check_decoded(from_json(src)) - _from_json_method.__doc__ = """ Decode a JSON object and create an instance of this class. @@ -325,8 +372,9 @@ def add_json_methods(description=None): Raises: PolygraphyException: If the JSON cannot be decoded to an instance of {cls} - """.format(cls=cls.__name__) - + """.format( + cls=cls.__name__ + ) cls.to_json = _to_json_method cls.from_json = staticmethod(_from_json_method) @@ -345,11 +393,9 @@ def add_json_methods(description=None): """ save_json(self, dest, description=description) - def _load_method(src): return check_decoded(load_json(src, description=description)) - _load_method.__doc__ = """ Loads an instance of this class from a JSON file. @@ -362,8 +408,9 @@ def add_json_methods(description=None): Raises: PolygraphyException: If the JSON cannot be decoded to an instance of {cls} - """.format(cls=cls.__name__) - + """.format( + cls=cls.__name__ + ) cls.save = _save_method cls.load = staticmethod(_load_method) diff --git a/tools/Polygraphy/polygraphy/logger/logger.py b/tools/Polygraphy/polygraphy/logger/logger.py index 631f34c7..a5365d7d 100644 --- a/tools/Polygraphy/polygraphy/logger/logger.py +++ b/tools/Polygraphy/polygraphy/logger/logger.py @@ -21,16 +21,21 @@ import time import traceback COLORED_MODULE_PRESENT = None + + def has_colors(): global COLORED_MODULE_PRESENT if COLORED_MODULE_PRESENT is None: try: import colored + COLORED_MODULE_PRESENT = True except: COLORED_MODULE_PRESENT = False - print("[W] 'colored' module is not installed, will not use colors when logging. " - "To enable colors, please install the 'colored' module: python3 -m pip install colored") + print( + "[W] 'colored' module is not installed, will not use colors when logging. " + "To enable colors, please install the 'colored' module: python3 -m pip install colored" + ) return COLORED_MODULE_PRESENT @@ -68,6 +73,7 @@ class LogMode(enum.IntEnum): """ Specifies how messages should be logged. """ + EACH = 0 """Log the message each time""" ONCE = 1 @@ -75,7 +81,7 @@ class LogMode(enum.IntEnum): class Logger(object): - ULTRA_VERBOSE = -20 # Cast it into the flames! + ULTRA_VERBOSE = -20 # Cast it into the flames! SUPER_VERBOSE = -10 EXTRA_VERBOSE = 0 VERBOSE = 10 @@ -87,16 +93,16 @@ class Logger(object): CRITICAL = 50 SEVERITY_LETTER_MAPPING = { - ULTRA_VERBOSE: "[U]", - SUPER_VERBOSE: "[S]", - EXTRA_VERBOSE: "[X]", - VERBOSE: "[V]", - INFO: "[I]", - START: "[I]", - FINISH: "[D]", - WARNING: "[W]", - ERROR: "[E]", - CRITICAL: "[!]", + ULTRA_VERBOSE: "[U]", + SUPER_VERBOSE: "[S]", + EXTRA_VERBOSE: "[X]", + VERBOSE: "[V]", + INFO: "[I]", + START: "[I]", + FINISH: "[I]", + WARNING: "[W]", + ERROR: "[E]", + CRITICAL: "[!]", } SEVERITY_COLOR_MAPPING = { @@ -140,7 +146,7 @@ class Logger(object): self._log_path = None self._log_file = None self.logging_indent = 0 - self.root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)) + self.root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)) self.once_logged = set() self.colors = colors self.letter = letter @@ -148,12 +154,10 @@ class Logger(object): self.line_info = line_info self.logger_callbacks = [] - @property def log_file(self): return self._log_path - @log_file.setter def log_file(self, value): self._log_path = value @@ -163,19 +167,16 @@ class Logger(object): os.makedirs(dir_path, exist_ok=True) self._log_file = open(self._log_path, "w") - @property def severity(self): return self._severity - @severity.setter def severity(self, value): self._severity = value for callback in self.logger_callbacks: callback(self._severity) - def register_callback(self, callback): """ Registers a callback with the logger, which will be invoked when the logging severity is modified. @@ -187,14 +188,12 @@ class Logger(object): callback(self._severity) self.logger_callbacks.append(callback) - def indent(self, level=1): """ Returns a context manager that indents all strings logged by the specified amount. """ return LoggerIndent(self, level + self.logging_indent) - def verbosity(self, severity=CRITICAL): """ Returns a context manager that temporarily changes the severity of the logger for its duration. @@ -205,7 +204,6 @@ class Logger(object): """ return LoggerVerbosity(self, severity) - def log(self, message, severity, mode=LogMode.EACH, stack_depth=2, error_ok=False): """ Logs a message to stdout. @@ -257,27 +255,24 @@ class Logger(object): prefix += get_line_info() return prefix - def apply_indentation(prefix, message): message_lines = str(message).splitlines() tab = constants.TAB * self.logging_indent newline_tab = "\n" + tab + " " * len(prefix) return tab + newline_tab.join([line for line in message_lines]) - def apply_color(message): if self.colors and has_colors(): import colored + color = Logger.SEVERITY_COLOR_MAPPING[severity] return colored.stylize(message, [colored.fg(color)]) if color else message return message - prefix = get_prefix() message = apply_indentation(prefix, message) return apply_color("{:}{:}".format(prefix, message)) - def should_log(message): should = severity >= self._severity if should and mode == LogMode.ONCE: @@ -286,7 +281,6 @@ class Logger(object): self.once_logged.add(message_hash) return should - if not should_log(message): return @@ -305,76 +299,63 @@ class Logger(object): # visible in the test result summary. if config.INTERNAL_CORRECTNESS_CHECKS and severity == Logger.WARNING: import warnings + warnings.warn(message) - file = sys.stdout if severity < Logger.WARNING else sys.stderr message = process_message(message, stack_depth=stack_depth) if self._log_file is not None: self._log_file.write(message + "\n") self._log_file.flush() - print(message, file=file) - + print(message) def backtrace(self, depth=0, limit=None, severity=ERROR): - limit = limit if limit is not None else (3 - self.severity // 10) * 2 # Info provides 1 stack frame + limit = limit if limit is not None else (3 - self.severity // 10) * 2 # Info provides 1 stack frame limit = max(limit, 0) self.log(" ".join(traceback.format_stack(f=sys._getframe(depth + 2), limit=limit)), severity=severity) - def ultra_verbose(self, message, mode=LogMode.EACH): self.log(message, Logger.ULTRA_VERBOSE, mode=mode, stack_depth=3, error_ok=True) - def super_verbose(self, message, mode=LogMode.EACH): self.log(message, Logger.SUPER_VERBOSE, mode=mode, stack_depth=3, error_ok=True) - def extra_verbose(self, message, mode=LogMode.EACH): self.log(message, Logger.EXTRA_VERBOSE, mode=mode, stack_depth=3, error_ok=True) - def verbose(self, message, mode=LogMode.EACH): self.log(message, Logger.VERBOSE, mode=mode, stack_depth=3, error_ok=True) - def info(self, message, mode=LogMode.EACH): self.log(message, Logger.INFO, mode=mode, stack_depth=3) - def start(self, message, mode=LogMode.EACH): self.log(message, Logger.START, mode=mode, stack_depth=3) - def finish(self, message, mode=LogMode.EACH): self.log(message, Logger.FINISH, mode=mode, stack_depth=3) - def warning(self, message, mode=LogMode.EACH): self.log(message, Logger.WARNING, mode=mode, stack_depth=3) - def error(self, message, mode=LogMode.EACH): self.log(message, Logger.ERROR, mode=mode, stack_depth=3) - def critical(self, message): self.log(message, Logger.CRITICAL, stack_depth=3) from polygraphy.exception import PolygraphyException + raise PolygraphyException(message) from None - def internal_error(self, message): - self.log(message, Logger.CRITICAL, stack_depth=3) - from polygraphy.exception import PolygraphyInternalException - raise PolygraphyInternalException(message) from None + from polygraphy import config + if config.INTERNAL_CORRECTNESS_CHECKS: + self.log(message, Logger.CRITICAL, stack_depth=3) + from polygraphy.exception import PolygraphyInternalException - def exit(self, message): - self.log(message, Logger.CRITICAL, stack_depth=3) - sys.exit(1) - + raise PolygraphyInternalException(message) from None def _str_from_module_info(self, module, name=None): ret = "" @@ -391,25 +372,33 @@ class Logger(object): try_append(lambda: " | Path: {:}".format(list(map(os.path.realpath, module.__path__)))) return ret - def module_info(self, module, name=None, severity=VERBOSE): G_LOGGER.log(self._str_from_module_info(module, name), severity=severity, mode=LogMode.ONCE) - def log_exception(self, func): """ Decorator that causes exceptions in a function to be logged. This is useful in cases where the exception is caught by a caller, but should still be logged. """ + def wrapped(*args, **kwargs): + from polygraphy.exception import PolygraphyException + try: return func(*args, **kwargs) + except PolygraphyException: + # `PolygraphyException`s are always logged. + raise except Exception as err: G_LOGGER.error(err) raise + return wrapped global G_LOGGER G_LOGGER = Logger() + +# For backwards compatibility +G_LOGGER.exit = G_LOGGER.critical diff --git a/tools/Polygraphy/polygraphy/mod/exporter.py b/tools/Polygraphy/polygraphy/mod/exporter.py index 2a06d0f1..7e757126 100644 --- a/tools/Polygraphy/polygraphy/mod/exporter.py +++ b/tools/Polygraphy/polygraphy/mod/exporter.py @@ -103,9 +103,8 @@ def export(funcify=False, func_name=None): for ancestor in hierarchy: if method in vars(ancestor): return vars(ancestor)[method] - else: - assert False, "Could not find method: {:} in the inheritance hierarcy of: {:}".format(method, symbol) + assert False, "Could not find method: {:} in the inheritance hierarcy of: {:}".format(method, symbol) def export_impl(func_or_cls): _add_to_all(func_or_cls.__name__, module) @@ -116,11 +115,17 @@ def export(funcify=False, func_name=None): from polygraphy.backend.base import BaseLoader assert inspect.isclass(func_or_cls), "Decorated type must be a loader to use funcify=True" - assert BaseLoader in inspect.getmro(func_or_cls), "Decorated type must derive from BaseLoader to use funcify=True" + assert BaseLoader in inspect.getmro( + func_or_cls + ), "Decorated type must derive from BaseLoader to use funcify=True" loader = func_or_cls def get_params(method): - return [p for p in inspect.signature(find_method(func_or_cls, method)).parameters.values() if p.name != "self"] + return [ + p + for p in inspect.signature(find_method(func_or_cls, method)).parameters.values() + if p.name != "self" + ] init_params = get_params("__init__") call_impl_params = get_params("call_impl") @@ -128,7 +133,9 @@ def export(funcify=False, func_name=None): def param_names(params): return list(str(p).partition("=")[0] for p in params) - assert (set(param_names(call_impl_params)) - set(param_names(init_params))) == set(param_names(call_impl_params)), "Cannot funcify a type where call_impl and __init__ have the same argument names!" + assert (set(param_names(call_impl_params)) - set(param_names(init_params))) == set( + param_names(call_impl_params) + ), "Cannot funcify a type where call_impl and __init__ have the same argument names!" # Dynamically generate a function with the right signature. @@ -137,7 +144,7 @@ def export(funcify=False, func_name=None): def is_special(param): return "*" in str(param) - def has_default(param): # Non special arguments that have default values + def has_default(param): # Non special arguments that have default values return "=" in str(param) def build_arg_list(should_include): @@ -160,15 +167,19 @@ def export(funcify=False, func_name=None): return loader_binding({init_args})({call_impl_args}) func_var = func_impl - """.format(signature=signature, init_args=init_args, call_impl_args=call_impl_args) + """.format( + signature=signature, init_args=init_args, call_impl_args=call_impl_args + ) ) - exec(func_code, {"loader_binding": loader}, locals()) # Need to bind the loader this way, or it won't be accesible from func_code. + exec( + func_code, {"loader_binding": loader}, locals() + ) # Need to bind the loader this way, or it won't be accesible from func_code. func = locals()["func_var"] # Next we setup the docstring so that it is a combination of the __init__ # and call_impl docstrings. - func.__doc__ = "Immediately evaluated functional variant of {}.\n".format(loader.__name__) + func.__doc__ = "Immediately evaluated functional variant of :class:`{}` .\n".format(loader.__name__) def try_add_method_doc(method): call_impl = find_method(loader, method) @@ -199,8 +210,12 @@ def warn_deprecated(name, use_instead, remove_in, module_name=None): G_LOGGER.internal_error("{:} should have been removed in version: {:}".format(name, remove_in)) full_obj_name = "{:}.{:}".format(module_name, name) if module_name else name - warnings.warn("{:} is deprecated and will be removed in Polygraphy {:}. " - "Use {:} instead.".format(full_obj_name, remove_in, use_instead), DeprecationWarning, stacklevel=3) + warnings.warn( + "{:} is deprecated and will be removed in Polygraphy {:}. " + "Use {:} instead.".format(full_obj_name, remove_in, use_instead), + DeprecationWarning, + stacklevel=3, + ) def deprecate(remove_in, use_instead, module_name=None, name=None): @@ -222,6 +237,7 @@ def deprecate(remove_in, use_instead, module_name=None, name=None): If not provided, this is automatically determined based on the decorated type. Defaults to None. """ + def deprecate_impl(obj): if config.INTERNAL_CORRECTNESS_CHECKS and version(polygraphy.__version__) >= version(remove_in): G_LOGGER.internal_error("{:} should have been removed in version: {:}".format(obj, remove_in)) @@ -230,6 +246,7 @@ def deprecate(remove_in, use_instead, module_name=None, name=None): name = name or obj.__name__ if inspect.ismodule(obj): + class DeprecatedModule(object): def __getattr__(self, attr_name): warn_deprecated(name, use_instead, remove_in, module_name) @@ -244,6 +261,7 @@ def deprecate(remove_in, use_instead, module_name=None, name=None): DeprecatedModule.__doc__ = "Deprecated: Use {:} instead".format(use_instead) return DeprecatedModule() elif inspect.isclass(obj): + class Deprecated(obj): def __init__(self, *args, **kwargs): warn_deprecated(name, use_instead, remove_in, module_name) @@ -252,9 +270,11 @@ def deprecate(remove_in, use_instead, module_name=None, name=None): Deprecated.__doc__ = "Deprecated: Use {:} instead".format(use_instead) return Deprecated elif inspect.isfunction(obj): + def wrapped(*args, **kwargs): warn_deprecated(name, use_instead, remove_in, module_name) return obj(*args, **kwargs) + wrapped.__doc__ = "Deprecated: Use {:} instead".format(use_instead) return wrapped else: @@ -290,7 +310,9 @@ def export_deprecated_alias(name, remove_in, use_instead=None): module = inspect.getmodule(sys._getframe(1)) def export_deprecated_alias_impl(obj): - new_obj = deprecate(remove_in, use_instead=use_instead or obj.__name__, module_name=module.__name__, name=name)(obj) + new_obj = deprecate(remove_in, use_instead=use_instead or obj.__name__, module_name=module.__name__, name=name)( + obj + ) _define_in_module(name, new_obj, module) _add_to_all(name, module) return obj diff --git a/tools/Polygraphy/polygraphy/mod/importer.py b/tools/Polygraphy/polygraphy/mod/importer.py index f24acac1..14744027 100644 --- a/tools/Polygraphy/polygraphy/mod/importer.py +++ b/tools/Polygraphy/polygraphy/mod/importer.py @@ -80,32 +80,38 @@ def lazy_import(name, log=True, version=None): A lazily loaded module. When an attribute is first accessed, the module will be imported. """ - assert version is None or version == LATEST_VERSION or any(version.startswith(char) for char in ["=", ">", "<"]), "version must be formatted as a version string!" + assert ( + version is None or version == LATEST_VERSION or any(version.startswith(char) for char in ["=", ">", "<"]) + ), "version must be formatted as a version string!" if "polygraphy" not in name: _all_external_lazy_imports.add(name) - def import_mod(): from polygraphy import config from polygraphy.logger import G_LOGGER, LogMode def install_mod(raise_error=True): - pkg = _MODULE_TO_PKG_NAME.get(name, name) - extra_flags = _MODULE_EXTRA_FLAGS.get(name, []) + modname = name.split(".")[0] + pkg = _MODULE_TO_PKG_NAME.get(modname, modname) + extra_flags = _MODULE_EXTRA_FLAGS.get(modname, []) if version == LATEST_VERSION: extra_flags.append("--upgrade") elif version is not None: pkg += version - cmd = [sys.executable, "-m", "pip", "install", pkg] + extra_flags - G_LOGGER.info("{:} is required, but not installed. Attempting to install now.\n" - "Running: {:}".format(pkg, " ".join(cmd))) + cmd = config.INSTALL_CMD + [pkg] + extra_flags + G_LOGGER.info( + "{:} is required, but not installed. Attempting to install now.\n" + "Running: {:}".format(pkg, " ".join(cmd)) + ) status = sp.run(cmd) if status.returncode != 0: - G_LOGGER.log("Could not automatically install required package: {:}. Please install it manually.".format(pkg), - severity=G_LOGGER.CRITICAL if raise_error else G_LOGGER.WARNING) + G_LOGGER.log( + "Could not automatically install required package: {:}. Please install it manually.".format(pkg), + severity=G_LOGGER.CRITICAL if raise_error else G_LOGGER.WARNING, + ) mod = importlib.import_module(name) return mod @@ -117,25 +123,32 @@ def lazy_import(name, log=True, version=None): if config.AUTOINSTALL_DEPS: mod = install_mod() else: - G_LOGGER.error("Module: {:} is required but could not be imported.\n" - "You can try setting POLYGRAPHY_AUTOINSTALL_DEPS=1 in your environment variables " - "to allow Polygraphy to automatically install missing packages.\n" - "Note that this may cause existing packages to be overwritten - hence, it may be " - "desirable to use a Python virtual environment or container. ".format(name)) + G_LOGGER.error( + "Module: {:} is required but could not be imported.\n" + "You can try setting POLYGRAPHY_AUTOINSTALL_DEPS=1 in your environment variables " + "to allow Polygraphy to automatically install missing packages.\n" + "Note that this may cause existing packages to be overwritten - hence, it may be " + "desirable to use a Python virtual environment or container. ".format(name) + ) raise # Auto-upgrade if necessary if version is not None and hasattr(mod, "__version__") and not _version_ok(mod.__version__, version): if config.AUTOINSTALL_DEPS: - G_LOGGER.info("Note: Package: '{name}' version {cur_ver} is installed, but version {rec_ver} is recommended.\n" - "Upgrading...".format(name=name, cur_ver=mod.__version__, rec_ver=version)) - mod = install_mod(raise_error=False) # We can try to use the other version if install fails. + G_LOGGER.info( + "Note: Package: '{name}' version {cur_ver} is installed, but version {rec_ver} is recommended.\n" + "Upgrading...".format(name=name, cur_ver=mod.__version__, rec_ver=version) + ) + mod = install_mod(raise_error=False) # We can try to use the other version if install fails. elif version != LATEST_VERSION: - G_LOGGER.warning("Package: '{name}' version {cur_ver} is installed, but version {rec_ver} is recommended.\n" - "Consider installing the recommended version or setting POLYGRAPHY_AUTOINSTALL_DEPS=1 in your " - "environment variables to do so automatically. ".format( - name=name, cur_ver=mod.__version__, rec_ver=version), - mode=LogMode.ONCE) + G_LOGGER.warning( + "Package: '{name}' version {cur_ver} is installed, but version {rec_ver} is recommended.\n" + "Consider installing the recommended version or setting POLYGRAPHY_AUTOINSTALL_DEPS=1 in your " + "environment variables to do so automatically. ".format( + name=name, cur_ver=mod.__version__, rec_ver=version + ), + mode=LogMode.ONCE, + ) if log: G_LOGGER.module_info(mod) @@ -147,7 +160,6 @@ def lazy_import(name, log=True, version=None): self = import_mod() return getattr(self, name) - def __setattr__(self, name, value): self = import_mod() return setattr(self, name, value) @@ -192,8 +204,10 @@ def import_from_script(path, name): sys.path.insert(0, dir) with contextlib.ExitStack() as stack: + def reset_sys_path(): del sys.path[0] + stack.callback(reset_sys_path) mod = importlib.import_module(modname) @@ -203,6 +217,8 @@ def import_from_script(path, name): ext = os.path.splitext(path)[1] err_msg = "Could not import symbol: {:} from script: {:}".format(name, path) if ext != ".py": - err_msg += "\nThis could be because the extension of the file is not '.py'. Note: The extension is: {:}".format(ext) + err_msg += "\nThis could be because the extension of the file is not '.py'. Note: The extension is: {:}".format( + ext + ) err_msg += "\nNote: Error was: {:}".format(err) G_LOGGER.critical(err_msg) diff --git a/tools/Polygraphy/polygraphy/mod/util.py b/tools/Polygraphy/polygraphy/mod/util.py index 6251113e..5e65bdb2 100644 --- a/tools/Polygraphy/polygraphy/mod/util.py +++ b/tools/Polygraphy/polygraphy/mod/util.py @@ -1,4 +1,3 @@ - # # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. # @@ -15,5 +14,6 @@ # limitations under the License. # + def version(version_str): return tuple([int(num) for num in version_str.split(".")]) diff --git a/tools/Polygraphy/polygraphy/tools/args/base.py b/tools/Polygraphy/polygraphy/tools/args/base.py index 95a5ec83..64a7b0e0 100644 --- a/tools/Polygraphy/polygraphy/tools/args/base.py +++ b/tools/Polygraphy/polygraphy/tools/args/base.py @@ -16,15 +16,16 @@ from polygraphy import util + class BaseArgs(object): """ Adds a arguments to a command-line parser, and provides capabilities to create Polygraphy objects based on the arguments. """ + def __init__(self, disable_abbrev=None): self.disable_abbrev = util.default(disable_abbrev, False) - def add_to_parser(self, parser): """ Add arguments to a command-line parser. @@ -34,7 +35,6 @@ class BaseArgs(object): """ pass - def parse(self, args): """ Parses relevant arguments from command-line arguments. @@ -44,7 +44,6 @@ class BaseArgs(object): """ pass - def register(self, maker): """ Registers another argument group with this one. @@ -55,7 +54,6 @@ class BaseArgs(object): """ pass - def check_registered(self): """ Called after all `register()` calls to make dependency checks easier. diff --git a/tools/Polygraphy/polygraphy/tools/args/comparator.py b/tools/Polygraphy/polygraphy/tools/args/comparator.py index 0069c69f..79e3e8b9 100644 --- a/tools/Polygraphy/polygraphy/tools/args/comparator.py +++ b/tools/Polygraphy/polygraphy/tools/args/comparator.py @@ -17,8 +17,7 @@ from polygraphy import mod, util from polygraphy.logger import G_LOGGER from polygraphy.tools.args import util as args_util from polygraphy.tools.args.base import BaseArgs -from polygraphy.tools.script import (inline, make_invocable, - make_invocable_if_nondefault, safe) +from polygraphy.tools.script import inline, make_invocable, make_invocable_if_nondefault, safe @mod.export() @@ -28,19 +27,40 @@ class ComparatorRunArgs(BaseArgs): self._iters = iters self._write = write - def add_to_parser(self, parser): - comparator_args = parser.add_argument_group("Comparator inference", "Options for running inference via Comparator.run()") + comparator_args = parser.add_argument_group( + "Comparator inference", "Options for running inference via Comparator.run()" + ) if self._iters: - comparator_args.add_argument("--warm-up", metavar="NUM", help="Number of warm-up runs before timing inference", type=int, default=None) - comparator_args.add_argument("--use-subprocess", help="Run runners in isolated subprocesses. Cannot be used with a debugger", - action="store_true", default=None) + comparator_args.add_argument( + "--warm-up", + metavar="NUM", + help="Number of warm-up runs before timing inference", + type=int, + default=None, + ) + comparator_args.add_argument( + "--use-subprocess", + help="Run runners in isolated subprocesses. Cannot be used with a debugger", + action="store_true", + default=None, + ) if self._write: - comparator_args.add_argument("--save-inputs", "--save-input-data", help="[EXPERIMENTAL] Path to save inference inputs. " - "The inputs (List[Dict[str, numpy.ndarray]]) will be encoded as JSON and saved", - default=None, dest="save_inputs") - comparator_args.add_argument("--save-outputs", "--save-results", help="Path to save results from runners. " - "The results (RunResults) will be encoded as JSON and saved", default=None, dest="save_results") + comparator_args.add_argument( + "--save-inputs", + "--save-input-data", + help="[EXPERIMENTAL] Path to save inference inputs. " + "The inputs (List[Dict[str, numpy.ndarray]]) will be encoded as JSON and saved", + default=None, + dest="save_inputs", + ) + comparator_args.add_argument( + "--save-outputs", + "--save-results", + help="Path to save results from runners. " "The results (RunResults) will be encoded as JSON and saved", + default=None, + dest="save_results", + ) def register(self, maker): from polygraphy.tools.args.data_loader import DataLoaderArgs @@ -48,33 +68,36 @@ class ComparatorRunArgs(BaseArgs): if isinstance(maker, DataLoaderArgs): self.data_loader_args = maker - def check_registered(self): assert self.data_loader_args is not None, "DataLoaderArgs is required for comparator!" - def parse(self, args): self.warm_up = args_util.get(args, "warm_up") self.use_subprocess = args_util.get(args, "use_subprocess") self.save_inputs = args_util.get(args, "save_inputs") self.save_results = args_util.get(args, "save_results") - def add_to_script(self, script): script.add_import(imports=["Comparator"], frm="polygraphy.comparator") RESULTS_VAR_NAME = inline(safe("results")) - comparator_run = make_invocable("Comparator.run", script.get_runners(), warm_up=self.warm_up, - data_loader=self.data_loader_args.add_to_script(script), - use_subprocess=self.use_subprocess, - save_inputs_path=self.save_inputs) + comparator_run = make_invocable( + "Comparator.run", + script.get_runners(), + warm_up=self.warm_up, + data_loader=self.data_loader_args.add_data_loader(script), + use_subprocess=self.use_subprocess, + save_inputs_path=self.save_inputs, + ) script.append_suffix(safe("\n# Runner Execution\n{results} = {:}", comparator_run, results=RESULTS_VAR_NAME)) if self.save_results: G_LOGGER.verbose("Will save runner results to: {:}".format(self.save_results)) script.add_import(imports=["util"], frm="polygraphy") - script.append_suffix(safe("\n# Save results\n{results}.save({:})", self.save_results, results=RESULTS_VAR_NAME)) + script.append_suffix( + safe("\n# Save results\n{results}.save({:})", self.save_results, results=RESULTS_VAR_NAME) + ) return RESULTS_VAR_NAME @@ -85,38 +108,72 @@ class ComparatorCompareArgs(BaseArgs): super().__init__() self._load = load - def add_to_parser(self, parser): comparator_args = parser.add_argument_group("Comparator comparisons", "Options for comparing inference results") - comparator_args.add_argument("--no-shape-check", help="Disable checking that output shapes match exactly", action="store_true", default=None) - comparator_args.add_argument("--rtol", "--rel-tol", dest="rtol", help="Relative tolerance for output comparison. " - "To specify per-output tolerances, use the format: --rtol [:]. If no output name is provided, " - "the tolerance is used for any outputs not explicitly specified. For example: " - "--rtol 1e-5 out0:1e-4 out1:1e-3", - nargs="+", default=None) - comparator_args.add_argument("--atol", "--abs-tol", dest="atol", help="Absolute tolerance for output comparison. " - "To specify per-output tolerances, use the format: --atol [:]. If no output name is provided, " - "the tolerance is used for any outputs not explicitly specified. For example: " - "--atol 1e-5 out0:1e-4 out1:1e-3", - nargs="+", default=None) - comparator_args.add_argument("--validate", help="Check outputs for NaNs and Infs", action="store_true", default=None) - comparator_args.add_argument("--fail-fast", help="Fail fast (stop comparing after the first failure)", action="store_true", default=None) - comparator_args.add_argument("--top-k", help="[EXPERIMENTAL] Apply Top-K (i.e. find indices of K largest values) to the outputs before comparing them." - "To specify per-output top-k, use the format: --top-k [:]. If no output name is provided, " - "top-k is applied to all outputs. For example: " - "--top-k out:5", - nargs="+", default=None) - comparator_args.add_argument("--check-error-stat", help="The error statistic to check. " - "For details on possible values, see the documentation for CompareFunc.basic_compare_func(). " - "To specify per-output values, use the format: --check-error-stat [:]. If no output name is provided, " - "the value is used for any outputs not explicitly specified. For example: " - "--check-error-stat max out0:mean out1:median", - nargs="+", default=None) + comparator_args.add_argument( + "--no-shape-check", + help="Disable checking that output shapes match exactly", + action="store_true", + default=None, + ) + comparator_args.add_argument( + "--rtol", + "--rel-tol", + dest="rtol", + help="Relative tolerance for output comparison. " + "To specify per-output tolerances, use the format: --rtol [:]. If no output name is provided, " + "the tolerance is used for any outputs not explicitly specified. For example: " + "--rtol 1e-5 out0:1e-4 out1:1e-3", + nargs="+", + default=None, + ) + comparator_args.add_argument( + "--atol", + "--abs-tol", + dest="atol", + help="Absolute tolerance for output comparison. " + "To specify per-output tolerances, use the format: --atol [:]. If no output name is provided, " + "the tolerance is used for any outputs not explicitly specified. For example: " + "--atol 1e-5 out0:1e-4 out1:1e-3", + nargs="+", + default=None, + ) + comparator_args.add_argument( + "--validate", help="Check outputs for NaNs and Infs", action="store_true", default=None + ) + comparator_args.add_argument( + "--fail-fast", help="Fail fast (stop comparing after the first failure)", action="store_true", default=None + ) + comparator_args.add_argument( + "--top-k", + help="[EXPERIMENTAL] Apply Top-K (i.e. find indices of K largest values) to the outputs before comparing them." + "To specify per-output top-k, use the format: --top-k [:]. If no output name is provided, " + "top-k is applied to all outputs. For example: " + "--top-k out:5", + nargs="+", + default=None, + ) + comparator_args.add_argument( + "--check-error-stat", + help="The error statistic to check. " + "For details on possible values, see the documentation for CompareFunc.basic_compare_func(). " + "To specify per-output values, use the format: --check-error-stat [:]. If no output name is provided, " + "the value is used for any outputs not explicitly specified. For example: " + "--check-error-stat max out0:mean out1:median", + nargs="+", + default=None, + ) if self._load: - comparator_args.add_argument("--load-outputs", "--load-results", help="Path(s) to load results from runners prior to comparing. " - "Each file should be a JSON-ified RunResults", nargs="+", default=[], dest="load_results") - + comparator_args.add_argument( + "--load-outputs", + "--load-results", + help="Path(s) to load results from runners prior to comparing. " + "Each file should be a JSON-ified RunResults", + nargs="+", + default=[], + dest="load_results", + ) def parse(self, args): self.no_shape_check = args_util.get(args, "no_shape_check") @@ -131,48 +188,70 @@ class ComparatorCompareArgs(BaseArgs): VALID_CHECK_ERROR_STATS = ["max", "mean", "median", "elemwise"] for stat in self.check_error_stat.values(): if stat not in VALID_CHECK_ERROR_STATS: - G_LOGGER.critical("Invalid choice for check_error_stat: {:}.\n" - "Note: Valid choices are: {:}".format(stat, VALID_CHECK_ERROR_STATS)) + G_LOGGER.critical( + "Invalid choice for check_error_stat: {:}.\n" + "Note: Valid choices are: {:}".format(stat, VALID_CHECK_ERROR_STATS) + ) # FIXME: This should be a proper dependency from a RunnerArgs self.runners = util.default(args_util.get(args, "runners"), []) - def add_to_script(self, script, results_name): script.add_import(imports=["Comparator"], frm="polygraphy.comparator") if self.load_results: script.add_import(imports=["util"], frm="polygraphy") script.add_import(imports=["RunResults"], frm="polygraphy.comparator") - script.append_suffix(safe("\n# Load results\nfor load_output in {:}:\n\t{results}.extend(RunResults.load(load_output))", - self.load_results, results=results_name)) + script.append_suffix( + safe( + "\n# Load results\nfor load_output in {:}:\n\t{results}.extend(RunResults.load(load_output))", + self.load_results, + results=results_name, + ) + ) if self.top_k is not None: script.add_import(imports=["PostprocessFunc"], frm="polygraphy.comparator") - script.append_suffix(safe("\n# Postprocessing - Apply Top-{top_k}\n" - "{results} = Comparator.postprocess({results}, PostprocessFunc.topk_func(k={top_k}))", - top_k=self.top_k, results=results_name)) + script.append_suffix( + safe( + "\n# Postprocessing - Apply Top-{top_k}\n" + "{results} = Comparator.postprocess({results}, PostprocessFunc.topk_func(k={top_k}))", + top_k=self.top_k, + results=results_name, + ) + ) SUCCESS_VAR_NAME = inline(safe("success")) script.append_suffix(safe("\n{success} = True", success=SUCCESS_VAR_NAME)) - if len(self.runners) > 1 or self.load_results: # Only do comparisons if there's actually something to compare. + if len(self.runners) > 1 or self.load_results: # Only do comparisons if there's actually something to compare. script.append_suffix(safe("# Accuracy Comparison")) - compare_func_str = make_invocable_if_nondefault("CompareFunc.basic_compare_func", rtol=self.rtol, atol=self.atol, - check_shapes=False if self.no_shape_check else None, - fail_fast=self.fail_fast, check_error_stat=self.check_error_stat) + compare_func_str = make_invocable_if_nondefault( + "CompareFunc.basic_compare_func", + rtol=self.rtol, + atol=self.atol, + check_shapes=False if self.no_shape_check else None, + fail_fast=self.fail_fast, + check_error_stat=self.check_error_stat, + ) compare_func = None if compare_func_str: script.add_import(imports=["CompareFunc"], frm="polygraphy.comparator") compare_func = inline(safe("compare_func")) script.append_suffix(safe("{:} = {:}", compare_func, compare_func_str)) - compare_accuracy = make_invocable("Comparator.compare_accuracy", results_name, compare_func=compare_func, - fail_fast=self.fail_fast) + compare_accuracy = make_invocable( + "Comparator.compare_accuracy", results_name, compare_func=compare_func, fail_fast=self.fail_fast + ) script.append_suffix(safe("{success} &= bool({:})\n", compare_accuracy, success=SUCCESS_VAR_NAME)) if self.validate: - script.append_suffix(safe("# Validation\n{success} &= Comparator.validate({results}, check_inf=True, check_nan=True)\n", - success=SUCCESS_VAR_NAME, results=results_name)) + script.append_suffix( + safe( + "# Validation\n{success} &= Comparator.validate({results}, check_inf=True, check_nan=True)\n", + success=SUCCESS_VAR_NAME, + results=results_name, + ) + ) return SUCCESS_VAR_NAME diff --git a/tools/Polygraphy/polygraphy/tools/args/data_loader.py b/tools/Polygraphy/polygraphy/tools/args/data_loader.py index 647bedea..cb16f6dc 100644 --- a/tools/Polygraphy/polygraphy/tools/args/data_loader.py +++ b/tools/Polygraphy/polygraphy/tools/args/data_loader.py @@ -17,8 +17,7 @@ from polygraphy import mod, util from polygraphy.tools.args import util as args_util from polygraphy.tools.args.base import BaseArgs -from polygraphy.tools.script import (Script, make_invocable, - make_invocable_if_nondefault, safe) +from polygraphy.tools.script import Script, make_invocable, make_invocable_if_nondefault, safe @mod.export() @@ -27,33 +26,78 @@ class DataLoaderArgs(BaseArgs): super().__init__() self.model_args = None - def add_to_parser(self, parser): - data_loader_args = parser.add_argument_group("Data Loader", "Options for controlling how input data is loaded or generated") - data_loader_args.add_argument("--seed", metavar="SEED", help="Seed to use for random inputs", - type=int, default=None) - data_loader_args.add_argument("--val-range", help="Range of values to generate in the data loader. " - "To specify per-input ranges, use the format: --val-range :[min,max]. " - "If no input name is provided, the range is used for any inputs not explicitly specified. " - "For example: --val-range [0,1] inp0:[2,50] inp1:[3.0,4.6]", - nargs="+", default=None) - data_loader_args.add_argument("--int-min", help="[DEPRECATED: Use --val-range] Minimum integer value for random integer inputs", type=int, default=None) - data_loader_args.add_argument("--int-max", help="[DEPRECATED: Use --val-range] Maximum integer value for random integer inputs", type=int, default=None) - data_loader_args.add_argument("--float-min", help="[DEPRECATED: Use --val-range] Minimum float value for random float inputs", type=float, default=None) - data_loader_args.add_argument("--float-max", help="[DEPRECATED: Use --val-range] Maximum float value for random float inputs", type=float, default=None) - data_loader_args.add_argument("--iterations", "--iters", metavar="NUM", help="Number of inference iterations for which to supply data", - type=int, default=None, dest="iterations") - data_loader_args.add_argument("--load-inputs", "--load-input-data", help="[EXPERIMENTAL] Path(s) to load inputs. The file(s) should be a JSON-ified " - "List[Dict[str, numpy.ndarray]], i.e. a list where each element is the feed_dict for a single iteration. " - "Other data loader options are ignored when this option is used", default=[], - dest="load_inputs", nargs="+") - data_loader_args.add_argument("--data-loader-script", help="Path to a Python script that defines a function that loads input data. " - "The function should take no arguments and return a generator or iterable that yields input data (Dict[str, np.ndarray]). " - "When this option is specified, all other data loader arguments are ignored. ", - default=None) - data_loader_args.add_argument("--data-loader-func-name", help="When using a data-loader-script, this specifies the name of the function " - "that loads data. Defaults to `load_data`. ", default="load_data") - + data_loader_args = parser.add_argument_group( + "Data Loader", "Options for controlling how input data is loaded or generated" + ) + data_loader_args.add_argument( + "--seed", metavar="SEED", help="Seed to use for random inputs", type=int, default=None + ) + data_loader_args.add_argument( + "--val-range", + help="Range of values to generate in the data loader. " + "To specify per-input ranges, use the format: --val-range :[min,max]. " + "If no input name is provided, the range is used for any inputs not explicitly specified. " + "For example: --val-range [0,1] inp0:[2,50] inp1:[3.0,4.6]", + nargs="+", + default=None, + ) + data_loader_args.add_argument( + "--int-min", + help="[DEPRECATED: Use --val-range] Minimum integer value for random integer inputs", + type=int, + default=None, + ) + data_loader_args.add_argument( + "--int-max", + help="[DEPRECATED: Use --val-range] Maximum integer value for random integer inputs", + type=int, + default=None, + ) + data_loader_args.add_argument( + "--float-min", + help="[DEPRECATED: Use --val-range] Minimum float value for random float inputs", + type=float, + default=None, + ) + data_loader_args.add_argument( + "--float-max", + help="[DEPRECATED: Use --val-range] Maximum float value for random float inputs", + type=float, + default=None, + ) + data_loader_args.add_argument( + "--iterations", + "--iters", + metavar="NUM", + help="Number of inference iterations for which to supply data", + type=int, + default=None, + dest="iterations", + ) + data_loader_args.add_argument( + "--load-inputs", + "--load-input-data", + help="[EXPERIMENTAL] Path(s) to load inputs. The file(s) should be a JSON-ified " + "List[Dict[str, numpy.ndarray]], i.e. a list where each element is the feed_dict for a single iteration. " + "Other data loader options are ignored when this option is used", + default=[], + dest="load_inputs", + nargs="+", + ) + data_loader_args.add_argument( + "--data-loader-script", + help="Path to a Python script that defines a function that loads input data. " + "The function should take no arguments and return a generator or iterable that yields input data (Dict[str, np.ndarray]). " + "When this option is specified, all other data loader arguments are ignored. ", + default=None, + ) + data_loader_args.add_argument( + "--data-loader-func-name", + help="When using a data-loader-script, this specifies the name of the function " + "that loads data. Defaults to `load_data`. ", + default="load_data", + ) def register(self, maker): from polygraphy.tools.args.model import ModelArgs @@ -61,7 +105,6 @@ class DataLoaderArgs(BaseArgs): if isinstance(maker, ModelArgs): self.model_args = maker - def parse(self, args): def omit_none_tuple(tup): if all([elem is None for elem in tup]): @@ -80,8 +123,44 @@ class DataLoaderArgs(BaseArgs): self.data_loader_script = args_util.get(args, "data_loader_script") self.data_loader_func_name = args_util.get(args, "data_loader_func_name") + def _add_to_script(self, script, user_input_metadata_str=None): + needs_invoke = False + if self.data_loader_script: + script.add_import(imports=["mod"], frm="polygraphy") + data_loader = make_invocable( + "mod.import_from_script", self.data_loader_script, name=self.data_loader_func_name + ) + needs_invoke = True + elif self.load_inputs: + script.add_import(imports=["load_json"], frm="polygraphy.json") + data_loader = safe( + "[]\nfor input_data_path in {load_inputs}:" + "\n\t{data_loader}.extend(load_json(input_data_path, description='input data'))", + load_inputs=self.load_inputs, + data_loader=Script.DATA_LOADER_NAME, + ) + else: + if user_input_metadata_str is None and self.model_args is not None and self.model_args.input_shapes: + user_input_metadata_str = self.model_args.input_shapes - def add_to_script(self, script, user_input_metadata_str=None): + if user_input_metadata_str: + script.add_import(imports=["TensorMetadata"], frm="polygraphy.common") + + data_loader = make_invocable_if_nondefault( + "DataLoader", + seed=self.seed, + iterations=self.iterations, + input_metadata=user_input_metadata_str, + int_range=self.int_range, + float_range=self.float_range, + val_range=self.val_range, + ) + if data_loader: + script.add_import(imports=["DataLoader"], frm="polygraphy.comparator") + + return script.set_data_loader(data_loader), needs_invoke + + def add_data_loader(self, script, *args, **kwargs): """ Adds a DataLoader to the script. @@ -90,31 +169,29 @@ class DataLoaderArgs(BaseArgs): The name of a variable containing TensorMetadata. This will control the shape and data type of the generated data. + + Returns: + str: The data loader, as a string. This may either be the variable name, + or an invocation of the data loader function. """ - if self.data_loader_script: - script.add_import(imports=["invoke_from_script"], frm="polygraphy.backend.common") - data_loader = make_invocable("invoke_from_script", self.data_loader_script, name=self.data_loader_func_name) - elif self.load_inputs: - script.add_import(imports=["load_json"], frm="polygraphy.json") - data_loader = safe("[]\nfor input_data_path in {load_inputs}:" - "\n\t{data_loader}.extend(load_json(input_data_path, description='input data'))", - load_inputs=self.load_inputs, data_loader=Script.DATA_LOADER_NAME) - else: - if user_input_metadata_str is None and self.model_args is not None and self.model_args.input_shapes: - user_input_metadata_str = self.model_args.input_shapes - - if user_input_metadata_str: - script.add_import(imports=["TensorMetadata"], frm="polygraphy.common") - - data_loader = make_invocable_if_nondefault("DataLoader", seed=self.seed, iterations=self.iterations, - input_metadata=user_input_metadata_str, int_range=self.int_range, float_range=self.float_range, - val_range=self.val_range) - if data_loader: - script.add_import(imports=["DataLoader"], frm="polygraphy.comparator") - - return script.set_data_loader(data_loader) - + data_loader, needs_invoke = self._add_to_script(script, *args, **kwargs) + if needs_invoke: + data_loader = make_invocable(data_loader) + return data_loader def get_data_loader(self, user_input_metadata=None): from polygraphy.comparator import DataLoader - return util.default(args_util.run_script(self.add_to_script, user_input_metadata), DataLoader()) + + needs_invoke = False + + # run_script expects the callable to return just the variable name, but self.add_to_script + # has 2 return values. We wrap it here to create a function with the right signature. + def add_to_script_wrapper(script, *args, **kwargs): + nonlocal needs_invoke + name, needs_invoke = self._add_to_script(script, *args, **kwargs) + return name + + data_loader = util.default(args_util.run_script(add_to_script_wrapper, user_input_metadata), DataLoader()) + if needs_invoke: + data_loader = data_loader() + return data_loader diff --git a/tools/Polygraphy/polygraphy/tools/args/logger.py b/tools/Polygraphy/polygraphy/tools/args/logger.py index 8d38b300..b479cce2 100644 --- a/tools/Polygraphy/polygraphy/tools/args/logger.py +++ b/tools/Polygraphy/polygraphy/tools/args/logger.py @@ -24,15 +24,36 @@ class LoggerArgs(BaseArgs): def add_to_parser(self, parser): logging_args = parser.add_argument_group("Logging", "Options for logging and debug output") - logging_args.add_argument("-v", "--verbose", help="Increase logging logging_args. Specify multiple times for higher verbosity", action="count", default=0) - logging_args.add_argument("-q", "--quiet", help="Decrease logging velogging_argsSpecify multiple times for lower verbosity", action="count", default=0) + logging_args.add_argument( + "-v", + "--verbose", + help="Increase logging verbosity. Specify multiple times for higher verbosity", + action="count", + default=0, + ) + logging_args.add_argument( + "-q", + "--quiet", + help="Decrease logging verbosity. Specify multiple times for lower verbosity", + action="count", + default=0, + ) logging_args.add_argument("--silent", help="Disable all output", action="store_true", default=None) - logging_args.add_argument("--log-format", help="Format for log messages: {{'timestamp': Include timestamp, 'line-info': Include file and line number, " - "'no-colors': Disable colors}}", choices=["timestamp", "line-info", "no-colors"], nargs="+", default=[]) - logging_args.add_argument("--log-file", help="Path to a file where Polygraphy logging output should be written. " - "This will not include logging output from dependencies, like TensorRT or ONNX-Runtime. ", default=None) - + logging_args.add_argument( + "--log-format", + help="Format for log messages: {{'timestamp': Include timestamp, 'line-info': Include file and line number, " + "'no-colors': Disable colors}}", + choices=["timestamp", "line-info", "no-colors"], + nargs="+", + default=[], + ) + logging_args.add_argument( + "--log-file", + help="Path to a file where Polygraphy logging output should be written. " + "This will not include logging output from dependencies, like TensorRT or ONNX-Runtime. ", + default=None, + ) def parse(self, args): self.verbosity_count = args_util.get(args, "verbose") - args_util.get(args, "quiet") @@ -43,7 +64,6 @@ class LoggerArgs(BaseArgs): # Enable logger settings immediately on parsing. self.get_logger() - def add_to_script(self, script): # Always required since it is used to print the exit message. script.append_preimport(safe("from polygraphy.logger import G_LOGGER")) @@ -57,8 +77,6 @@ class LoggerArgs(BaseArgs): logger_settings.append("G_LOGGER.severity = G_LOGGER.EXTRA_VERBOSE") elif self.verbosity_count == 1: logger_settings.append("G_LOGGER.severity = G_LOGGER.VERBOSE") - elif self.verbosity_count == 0: - logger_settings.append("G_LOGGER.severity = G_LOGGER.INFO") elif self.verbosity_count == -1: logger_settings.append("G_LOGGER.severity = G_LOGGER.START") elif self.verbosity_count == -2: @@ -70,7 +88,6 @@ class LoggerArgs(BaseArgs): elif self.verbosity_count <= -4: logger_settings.append("G_LOGGER.severity = G_LOGGER.CRITICAL") - if self.silent: logger_settings.append("G_LOGGER.severity = G_LOGGER.CRITICAL") @@ -90,6 +107,5 @@ class LoggerArgs(BaseArgs): return safe("G_LOGGER") - def get_logger(self): return args_util.run_script(self.add_to_script) diff --git a/tools/Polygraphy/polygraphy/tools/args/model.py b/tools/Polygraphy/polygraphy/tools/args/model.py index 6f9c775a..52cc2e1c 100644 --- a/tools/Polygraphy/polygraphy/tools/args/model.py +++ b/tools/Polygraphy/polygraphy/tools/args/model.py @@ -32,7 +32,7 @@ class ModelArgs(BaseArgs): ".engine": "engine", ".plan": "engine", ".graphdef": "frozen", - ".py": "trt-network-script" + ".py": "trt-network-script", } class ModelType(str): @@ -47,19 +47,15 @@ class ModelArgs(BaseArgs): assert model_type in ModelArgs.ModelType.VALID_TYPES or model_type is None return str.__new__(cls, model_type) - def is_tf(self): return self in ModelArgs.ModelType.TF_TYPES - def is_onnx(self): return self in ModelArgs.ModelType.ONNX_TYPES - def is_trt(self): return self in ModelArgs.ModelType.TRT_TYPES - def __init__(self, model_required=False, inputs="--inputs", model_type=None): super().__init__() self._model_required = model_required @@ -67,25 +63,33 @@ class ModelArgs(BaseArgs): # If model type is provided, it means the tool only supports a single type of model. self._model_type = model_type - def add_to_parser(self, parser): model_args = parser.add_argument_group("Model", "Options for the model") - model_args.add_argument("model_file", help="Path to the model", nargs=None if self._model_required else '?') + model_args.add_argument("model_file", help="Path to the model", nargs=None if self._model_required else "?") if self._model_type is None: - model_args.add_argument("--model-type", help="The type of the input model: {{'frozen': TensorFlow frozen graph, 'keras': Keras model, " - "'ckpt': TensorFlow checkpoint directory, 'onnx': ONNX model, 'engine': TensorRT engine, 'trt-network-script': " - "A Python script that defines a `load_network` function that takes no arguments and returns a TensorRT Builder, " - "Network, and optionally Parser, " - "'uff': UFF file [deprecated], 'caffe': Caffe prototxt [deprecated]}}", - choices=ModelArgs.ModelType.VALID_TYPES, - default=None) + model_args.add_argument( + "--model-type", + help="The type of the input model: {{'frozen': TensorFlow frozen graph, 'keras': Keras model, " + "'ckpt': TensorFlow checkpoint directory, 'onnx': ONNX model, 'engine': TensorRT engine, 'trt-network-script': " + "A Python script that defines a `load_network` function that takes no arguments and returns a TensorRT Builder, " + "Network, and optionally Parser, " + "'uff': UFF file [deprecated], 'caffe': Caffe prototxt [deprecated]}}", + choices=ModelArgs.ModelType.VALID_TYPES, + default=None, + ) if self._inputs: - model_args.add_argument(self._inputs.replace("inputs", "input") + "-shapes", self._inputs, - help="Model input(s) and their shape(s). " - "Format: {arg_name}-shapes :. " - "For example: {arg_name}-shapes image:[1,3,224,224] other_input:[10]".format( - arg_name=self._inputs.replace("inputs", "input")), nargs="+", default=None, dest="input_shapes") - + model_args.add_argument( + self._inputs.replace("inputs", "input") + "-shapes", + self._inputs, + help="Model input(s) and their shape(s). Generally, this is used to determine inference-time input shapes, " + "or override dynamic shapes set in the model. Format: {arg_name}-shapes :. " + "For example: {arg_name}-shapes image:[1,3,224,224] other_input:[10]".format( + arg_name=self._inputs.replace("inputs", "input") + ), + nargs="+", + default=None, + dest="input_shapes", + ) def parse(self, args): def determine_model_type(): @@ -112,16 +116,18 @@ class ModelArgs(BaseArgs): if model_type: return model_type - G_LOGGER.exit("Could not automatically determine model type for: {:}\n" - "Please explicitly specify the type with the --model-type option".format(args.model_file)) - + G_LOGGER.critical( + "Could not automatically determine model type for: {:}\n" + "Please explicitly specify the type with the --model-type option".format(args.model_file) + ) if args_util.get(args, "input_shapes"): - self.input_shapes = args_util.parse_meta(args_util.get(args, "input_shapes"), includes_dtype=False) # TensorMetadata + self.input_shapes = args_util.parse_meta( + args_util.get(args, "input_shapes"), includes_dtype=False + ) # TensorMetadata else: self.input_shapes = TensorMetadata() - self.model_file = args_util.get(args, "model_file") if self.model_file: @@ -130,9 +136,11 @@ class ModelArgs(BaseArgs): G_LOGGER.warning("Model path does not exist: {:}".format(self.model_file)) self.model_file = os.path.abspath(self.model_file) - model_type_str = util.default(self._model_type, determine_model_type()) + model_type_str = self._model_type if self._model_type else determine_model_type() self.model_type = ModelArgs.ModelType(model_type_str) if model_type_str else None if self.model_type == "trt-network-script" and (not self.model_file or not self.model_file.endswith(".py")): - G_LOGGER.exit("TensorRT network scripts must exist and have '.py' extensions. " - "Note: Provided network script path was: {:}".format(self.model_file)) + G_LOGGER.critical( + "TensorRT network scripts must exist and have '.py' extensions. " + "Note: Provided network script path was: {:}".format(self.model_file) + ) diff --git a/tools/Polygraphy/polygraphy/tools/args/onnx/loader.py b/tools/Polygraphy/polygraphy/tools/args/onnx/loader.py index e317ce38..9eaed0f5 100644 --- a/tools/Polygraphy/polygraphy/tools/args/onnx/loader.py +++ b/tools/Polygraphy/polygraphy/tools/args/onnx/loader.py @@ -13,10 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # -import os -import tempfile -from collections import OrderedDict - from polygraphy import constants, mod, util from polygraphy.common import TensorMetadata from polygraphy.logger import G_LOGGER @@ -38,39 +34,77 @@ class OnnxSaveArgs(BaseArgs): self._required = required self.onnx_shape_inference_args = None - def register(self, maker): if self._infer_shapes and isinstance(maker, OnnxShapeInferenceArgs): self.onnx_shape_inference_args = maker - def add_to_parser(self, parser): self.group = parser.add_argument_group("ONNX Save Options", "Options for saving ONNX models") if self._output: flag = "--{:}".format(self._output) short = self._short_opt or flag - self.group.add_argument(short, flag, help="Path to save the ONNX model", dest="save_onnx", default=None, required=self._required) - - self.group.add_argument("--save-external-data", help="Path to save external data for the ONNX model", default=None) + self.group.add_argument( + short, flag, help="Path to save the ONNX model", dest="save_onnx", default=None, required=self._required + ) + self.group.add_argument( + "--save-external-data", + help="Whether to save weight data in external file(s). " + "To use a non-default path, supply the desired path as an argument. This is always a relative path; " + "external data is always written to the same directory as the model. ", + default=None, + action="append", + nargs="?", + ) + self.group.add_argument( + "--external-data-size-threshold", + help="The size threshold, in bytes, above which tensor data will be stored in the external file. " + "Tensors smaller that this threshold will remain in the ONNX file. " + "Has no effect if `--save-external-data` is not set", + default=None, + type=int, + ) + self.group.add_argument( + "--no-save-all-tensors-to-one-file", + help="Do not save all tensors to a single file when saving external data. " + "Has no effect if `--save-external-data` is not set", + dest="all_tensors_to_one_file", + default=None, + action="store_false", + ) def parse(self, args): self.path = args_util.get(args, "save_onnx") - self.save_external_data = args_util.get(args, "save_external_data") + save_external_data = args_util.get(args, "save_external_data") + if save_external_data is not None: + save_external_data = save_external_data[0] or "" + self.save_external_data = save_external_data + self.size_threshold = args_util.get(args, "external_data_size_threshold") + self.all_tensors_to_one_file = args_util.get(args, "all_tensors_to_one_file") def add_save_onnx(self, script, loader_name): if self.path is None: return loader_name - script.add_import(imports=["SaveOnnx"], frm="polygraphy.backend.onnx") - loader_name = script.add_loader(make_invocable("SaveOnnx", loader_name, path=self.path, external_data_path=self.save_external_data), "save_onnx") - # Need to run shape inference again after processing the graph since it may have changed. if self.onnx_shape_inference_args is not None: loader_name = self.onnx_shape_inference_args.add_to_script(script, loader_name) - return loader_name + script.add_import(imports=["SaveOnnx"], frm="polygraphy.backend.onnx") + loader_name = script.add_loader( + make_invocable( + "SaveOnnx", + loader_name, + path=self.path, + external_data_path=self.save_external_data, + size_threshold=self.size_threshold, + all_tensors_to_one_file=self.all_tensors_to_one_file, + ), + "save_onnx", + ) + + return loader_name def save_onnx(self, model, path=None): with util.TempAttrChange(self, "path", path): @@ -86,36 +120,51 @@ class OnnxShapeInferenceArgs(BaseArgs): super().__init__() self._default = default self._enable_force_fallback = enable_force_fallback - + self.onnx_loader_args = None def add_to_parser(self, parser): self.group = parser.add_argument_group("ONNX Shape Inference", "Options for ONNX Shape Inference") g = self.group.add_mutually_exclusive_group() if self._default: - g.add_argument("--no-shape-inference", help="Disable ONNX shape inference when loading the model", - dest="do_shape_inference", action="store_false", default=True) + g.add_argument( + "--no-shape-inference", + help="Disable ONNX shape inference when loading the model", + dest="do_shape_inference", + action="store_false", + default=True, + ) else: - g.add_argument("--shape-inference", help="Enable ONNX shape inference when loading the model", - dest="do_shape_inference", action="store_true", default=False) + g.add_argument( + "--shape-inference", + help="Enable ONNX shape inference when loading the model", + dest="do_shape_inference", + action="store_true", + default=False, + ) if self._enable_force_fallback: - g.add_argument("--force-fallback-shape-inference", help="Force Polygraphy to use ONNX-Runtime to determine metadata for " - "tensors in the graph. This can be useful in cases where ONNX shape inference does not generate correct information. " - "Note that this will cause dynamic dimensions to become fixed. ", - action="store_true", default=None) - + g.add_argument( + "--force-fallback-shape-inference", + help="Force Polygraphy to use ONNX-Runtime to determine metadata for " + "tensors in the graph. This can be useful in cases where ONNX shape inference does not generate correct information. " + "Note that this will cause dynamic dimensions to become fixed. ", + action="store_true", + default=None, + ) def register(self, maker): from polygraphy.tools.args.data_loader import DataLoaderArgs if isinstance(maker, DataLoaderArgs): self.data_loader_args = maker - + if isinstance(maker, OnnxLoaderArgs): + self.onnx_loader_args = maker def check_registered(self): - assert not self._enable_force_fallback or self.data_loader_args, "DataLoaderArgs is required if force fallback shape inference is enabled!" - + assert ( + not self._enable_force_fallback or self.data_loader_args + ), "DataLoaderArgs is required if force fallback shape inference is enabled!" def parse(self, args): self.do_shape_inference = args_util.get(args, "do_shape_inference") @@ -125,14 +174,15 @@ class OnnxShapeInferenceArgs(BaseArgs): if self.force_fallback: self.do_shape_inference = False - def add_to_script(self, script, loader_name): if self.do_shape_inference: script.add_import(imports=["InferShapes"], frm="polygraphy.backend.onnx") - loader_name = script.add_loader(make_invocable("InferShapes", loader_name), "infer_shapes") + external_data_dir = self.onnx_loader_args.load_external_data if self.onnx_loader_args is not None else None + loader_name = script.add_loader( + make_invocable("InferShapes", loader_name, external_data_dir=external_data_dir), "infer_shapes" + ) return loader_name - def fallback_inference(self, onnx_model): """ Run inference with ONNX-Runtime. @@ -156,7 +206,9 @@ class OnnxShapeInferenceArgs(BaseArgs): with G_LOGGER.verbosity(G_LOGGER.severity + 10): load_model = onnx_backend.ModifyOutputs(onnx_model, outputs=constants.MARK_ALL, copy=True) - with onnxrt_backend.OnnxrtRunner(onnxrt_backend.SessionFromOnnx(onnx_backend.BytesFromOnnx(load_model))) as runner: + with onnxrt_backend.OnnxrtRunner( + onnxrt_backend.SessionFromOnnx(onnx_backend.BytesFromOnnx(load_model)) + ) as runner: # We want to set input_metadata only - not user_input_metadata, so that user_input_metadata # will be populated by the --model-inputs argument. data_loader = self.data_loader_args.get_data_loader() @@ -164,8 +216,11 @@ class OnnxShapeInferenceArgs(BaseArgs): feed_dict = data_loader[0] with G_LOGGER.verbosity(G_LOGGER.severity - 10): - G_LOGGER.info("Running fallback shape inference using input metadata:\n{:}".format( - TensorMetadata.from_feed_dict(feed_dict))) + G_LOGGER.info( + "Running fallback shape inference using input metadata:\n{:}".format( + TensorMetadata.from_feed_dict(feed_dict) + ) + ) outputs = runner.infer(feed_dict) # We include the inputs here so that we have values for all tensors in the model. @@ -186,20 +241,32 @@ class OnnxLoaderArgs(BaseArgs): self._save = save self._output_prefix = output_prefix - def add_to_parser(self, parser): self.group = parser.add_argument_group("ONNX Loader", "Options for the ONNX Loader") - self.group.add_argument("--ext", "--load-external-data", dest="load_external_data", - help="Path to a directory containing external data for the model. ") + self.group.add_argument( + "--external-data-dir", + "--load-external-data", + "--ext", + dest="load_external_data", + help="Path to a directory containing external data for the model. ", + ) if self._output_prefix is not None: - self.group.add_argument("--{:}outputs".format(self._output_prefix), help="Name(s) of ONNX tensor(s) to mark as output(s). " - "Using the special value 'mark all' indicates that all tensors should be used as outputs", - nargs="+", default=None, dest="onnx_outputs") - self.group.add_argument("--{:}exclude-outputs".format(self._output_prefix), - help="[EXPERIMENTAL] Name(s) of ONNX output(s) to unmark as outputs.", - nargs="+", default=None, dest="onnx_exclude_outputs") - + self.group.add_argument( + "--{:}outputs".format(self._output_prefix), + help="Name(s) of ONNX tensor(s) to mark as output(s). " + "Using the special value 'mark all' indicates that all tensors should be used as outputs", + nargs="+", + default=None, + dest="onnx_outputs", + ) + self.group.add_argument( + "--{:}exclude-outputs".format(self._output_prefix), + help="[EXPERIMENTAL] Name(s) of ONNX output(s) to unmark as outputs.", + nargs="+", + default=None, + dest="onnx_exclude_outputs", + ) def register(self, maker): from polygraphy.tools.args.model import ModelArgs @@ -214,18 +281,15 @@ class OnnxLoaderArgs(BaseArgs): if isinstance(maker, OnnxShapeInferenceArgs): self.onnx_shape_inference_args = maker - def check_registered(self): assert self.model_args is not None, "ModelArgs is required!" assert not self._save or self.onnx_save_args is not None, "OnnxSaveArgs is required to use save=True" - def parse(self, args): self.outputs = args_util.get_outputs(args, "onnx_outputs") self.exclude_outputs = args_util.get(args, "onnx_exclude_outputs") self.load_external_data = args_util.get(args, "load_external_data") - def _get_modify_onnx_loader(self, script, loader_name, disable_custom_outputs=None): if disable_custom_outputs: outputs = None @@ -236,27 +300,32 @@ class OnnxLoaderArgs(BaseArgs): if outputs or exclude_outputs: script.add_import(imports=["ModifyOutputs as ModifyOnnxOutputs"], frm="polygraphy.backend.onnx") - loader_name = script.add_loader(make_invocable("ModifyOnnxOutputs", loader_name, - outputs=outputs, exclude_outputs=exclude_outputs), "modify_outputs") - - if self.onnx_shape_inference_args is not None: - loader_name = self.onnx_shape_inference_args.add_to_script(script, loader_name) + loader_name = script.add_loader( + make_invocable("ModifyOnnxOutputs", loader_name, outputs=outputs, exclude_outputs=exclude_outputs), + "modify_outputs", + ) return loader_name - def add_onnx_loader(self, script, disable_custom_outputs=None, suffix=None): model_type = self.model_args.model_type if model_type.is_onnx(): - script.add_import(imports=["OnnxFromPath"], frm="polygraphy.backend.onnx") - loader_str = make_invocable("OnnxFromPath", self.model_args.model_file, external_data_dir=self.load_external_data) - loader_name = script.add_loader(loader_str, "load_onnx", suffix=suffix) + loader_name = self.model_args.model_file + if self.onnx_shape_inference_args is not None: + loader_name = self.onnx_shape_inference_args.add_to_script(script, loader_name) + + if loader_name == self.model_args.model_file: # Shape inference loader isn't being used, have to load. + script.add_import(imports=["OnnxFromPath"], frm="polygraphy.backend.onnx") + loader_str = make_invocable( + "OnnxFromPath", self.model_args.model_file, external_data_dir=self.load_external_data + ) + loader_name = script.add_loader(loader_str, "load_onnx", suffix=suffix) elif model_type.is_tf(): if self.tf2onnx_loader_args is None: - G_LOGGER.exit("Could not load: {:}. Is it an ONNX model?".format(self.model_args.model_file)) + G_LOGGER.critical("Could not load: {:}. Is it an ONNX model?".format(self.model_args.model_file)) loader_name = self.tf2onnx_loader_args.add_to_script(script) else: - G_LOGGER.exit("Model type: {:} cannot be converted to ONNX.".format(model_type)) + G_LOGGER.critical("Model type: {:} cannot be converted to ONNX.".format(model_type)) loader_name = self._get_modify_onnx_loader(script, loader_name, disable_custom_outputs=disable_custom_outputs) @@ -265,7 +334,6 @@ class OnnxLoaderArgs(BaseArgs): return loader_name - def should_use_onnx_loader(self, disable_custom_outputs=None): """ Whether this model needs to be loaded via a Polygraphy ONNX loader, e.g., in case it @@ -274,16 +342,24 @@ class OnnxLoaderArgs(BaseArgs): tmp_script = Script() inp_loader = "check_needs_modify" needs_modify = self._get_modify_onnx_loader(tmp_script, inp_loader, disable_custom_outputs) != inp_loader + needs_shape_inference = ( + self.onnx_shape_inference_args is not None and self.onnx_shape_inference_args.do_shape_inference + ) + needs_save = self.onnx_save_args is not None and self.onnx_save_args.path is not None # Currently, other loaders do not support external data, so we must fall back to the ONNX loader if it's present. - return not self.model_args.model_type.is_onnx() or needs_modify or self.load_external_data - + return ( + not self.model_args.model_type.is_onnx() + or needs_modify + or self.load_external_data + or needs_shape_inference + or needs_save + ) def add_serialized_onnx_loader(self, script, disable_custom_outputs=None): script.add_import(imports=["BytesFromOnnx"], frm="polygraphy.backend.onnx") onnx_loader = self.add_onnx_loader(script, disable_custom_outputs=disable_custom_outputs) return script.add_loader(make_invocable("BytesFromOnnx", onnx_loader), "serialize_onnx") - def load_onnx(self): loader = args_util.run_script(self.add_onnx_loader) return loader() diff --git a/tools/Polygraphy/polygraphy/tools/args/onnxrt/runner.py b/tools/Polygraphy/polygraphy/tools/args/onnxrt/runner.py index 7d616438..5477f2a5 100644 --- a/tools/Polygraphy/polygraphy/tools/args/onnxrt/runner.py +++ b/tools/Polygraphy/polygraphy/tools/args/onnxrt/runner.py @@ -29,12 +29,10 @@ class OnnxrtRunnerArgs(BaseArgs): if isinstance(maker, ModelArgs): self.model_args = maker - def check_registered(self): assert self.onnx_loader_args is not None, "OnnxLoaderArgs is required!" assert self.model_args is not None, "ModelArgs is required!" - def add_to_script(self, script): script.add_import(imports=["OnnxrtRunner"], frm="polygraphy.backend.onnxrt") if self.onnx_loader_args.should_use_onnx_loader(): diff --git a/tools/Polygraphy/polygraphy/tools/args/tf/config.py b/tools/Polygraphy/polygraphy/tools/args/tf/config.py index 8dec738d..c29f699b 100644 --- a/tools/Polygraphy/polygraphy/tools/args/tf/config.py +++ b/tools/Polygraphy/polygraphy/tools/args/tf/config.py @@ -23,21 +23,34 @@ from polygraphy.tools.script import make_invocable_if_nondefault @mod.export() class TfConfigArgs(BaseArgs): def add_to_parser(self, parser): - tf_args = parser.add_argument_group("TensorFlow Session Configuration", "Options for the TensorFlow Session Configuration") - tf_args.add_argument("--gpu-memory-fraction", help="Maximum percentage of GPU memory TensorFlow can allocate per process", type=float, default=None) - tf_args.add_argument("--allow-growth", help="Allow GPU memory allocated by TensorFlow to grow", action="store_true", default=None) - tf_args.add_argument("--xla", help="[EXPERIMENTAL] Attempt to run graph with xla", action="store_true", default=None) - + tf_args = parser.add_argument_group( + "TensorFlow Session Configuration", "Options for the TensorFlow Session Configuration" + ) + tf_args.add_argument( + "--gpu-memory-fraction", + help="Maximum percentage of GPU memory TensorFlow can allocate per process", + type=float, + default=None, + ) + tf_args.add_argument( + "--allow-growth", help="Allow GPU memory allocated by TensorFlow to grow", action="store_true", default=None + ) + tf_args.add_argument( + "--xla", help="[EXPERIMENTAL] Attempt to run graph with xla", action="store_true", default=None + ) def parse(self, args): self.gpu_memory_fraction = args_util.get(args, "gpu_memory_fraction") self.allow_growth = args_util.get(args, "allow_growth") self.xla = args_util.get(args, "xla") - def add_to_script(self, script): - config_loader_str = make_invocable_if_nondefault("CreateConfig", gpu_memory_fraction=self.gpu_memory_fraction, - allow_growth=self.allow_growth, use_xla=self.xla) + config_loader_str = make_invocable_if_nondefault( + "CreateConfig", + gpu_memory_fraction=self.gpu_memory_fraction, + allow_growth=self.allow_growth, + use_xla=self.xla, + ) if config_loader_str is not None: script.add_import(imports=["CreateConfig"], frm="polygraphy.backend.tf") config_loader_name = script.add_loader(config_loader_str, "create_tf_config") diff --git a/tools/Polygraphy/polygraphy/tools/args/tf/loader.py b/tools/Polygraphy/polygraphy/tools/args/tf/loader.py index 2b88cf4b..05b8a742 100644 --- a/tools/Polygraphy/polygraphy/tools/args/tf/loader.py +++ b/tools/Polygraphy/polygraphy/tools/args/tf/loader.py @@ -28,29 +28,58 @@ class TfLoaderArgs(BaseArgs): self._enable_artifacts = artifacts self._enable_outputs = outputs - def add_to_parser(self, parser): tf_args = parser.add_argument_group("TensorFlow Loader", "Options for TensorFlow Loader") - tf_args.add_argument("--ckpt", help="[EXPERIMENTAL] Name of the checkpoint to load. Required if the `checkpoint` file is missing. Should not include file extension " - "(e.g. to load `model.meta` use `--ckpt=model`)", default=None) + tf_args.add_argument( + "--ckpt", + help="[EXPERIMENTAL] Name of the checkpoint to load. Required if the `checkpoint` file is missing. Should not include file extension " + "(e.g. to load `model.meta` use `--ckpt=model`)", + default=None, + ) if self._enable_outputs: - tf_args.add_argument("--tf-outputs", help="Name(s) of TensorFlow output(s). " - "Using '--tf-outputs mark all' indicates that all tensors should be used as outputs", nargs="+", default=None) + tf_args.add_argument( + "--tf-outputs", + help="Name(s) of TensorFlow output(s). " + "Using '--tf-outputs mark all' indicates that all tensors should be used as outputs", + nargs="+", + default=None, + ) if self._enable_artifacts: tf_args.add_argument("--save-pb", help="Path to save the TensorFlow frozen graphdef", default=None) - tf_args.add_argument("--save-tensorboard", help="[EXPERIMENTAL] Path to save a TensorBoard visualization", default=None) - tf_args.add_argument("--freeze-graph", help="[EXPERIMENTAL] Attempt to freeze the graph", action="store_true", default=None) + tf_args.add_argument( + "--save-tensorboard", help="[EXPERIMENTAL] Path to save a TensorBoard visualization", default=None + ) + tf_args.add_argument( + "--freeze-graph", help="[EXPERIMENTAL] Attempt to freeze the graph", action="store_true", default=None + ) if self._enable_tftrt: - tftrt_args = parser.add_argument_group("TensorFlow-TensorRT", "[UNTESTED] Options for TensorFlow-TensorRT Integration") - tftrt_args.add_argument("--tftrt", "--use-tftrt", help="[UNTESTED] Enable TF-TRT integration", action="store_true", default=None, dest="tftrt") - tftrt_args.add_argument("--minimum-segment-size", help="Minimum length of a segment to convert to TensorRT", type=int, default=None) - tftrt_args.add_argument("--dynamic-op", help="Enable dynamic mode (defers engine build until runtime)", action="store_true", default=None) - + tftrt_args = parser.add_argument_group( + "TensorFlow-TensorRT", "[UNTESTED] Options for TensorFlow-TensorRT Integration" + ) + tftrt_args.add_argument( + "--tftrt", + "--use-tftrt", + help="[UNTESTED] Enable TF-TRT integration", + action="store_true", + default=None, + dest="tftrt", + ) + tftrt_args.add_argument( + "--minimum-segment-size", + help="Minimum length of a segment to convert to TensorRT", + type=int, + default=None, + ) + tftrt_args.add_argument( + "--dynamic-op", + help="Enable dynamic mode (defers engine build until runtime)", + action="store_true", + default=None, + ) def register(self, maker): from polygraphy.tools.args.model import ModelArgs - from polygraphy.tools.args.trt import (TrtConfigArgs, - TrtEngineSaveArgs) + from polygraphy.tools.args.trt import TrtConfigArgs, TrtEngineSaveArgs from polygraphy.tools.args.trt_legacy import TrtLegacyArgs if isinstance(maker, ModelArgs): @@ -65,13 +94,11 @@ class TfLoaderArgs(BaseArgs): if isinstance(maker, TrtEngineSaveArgs): self.trt_engine_save_args = maker - def check_registered(self): assert self.model_args is not None, "ModelArgs is required!" if self._enable_tftrt: assert self.trt_config_args is not None, "TrtConfigArgs is required when tftrt is enabled!" - def parse(self, args): self.ckpt = args_util.get(args, "ckpt") self.outputs = args_util.get_outputs(args, "tf_outputs") @@ -82,7 +109,6 @@ class TfLoaderArgs(BaseArgs): self.minimum_segment_size = args_util.get(args, "minimum_segment_size") self.dynamic_op = args_util.get(args, "dynamic_op") - def add_to_script(self, script, disable_custom_outputs=None, suffix=None): if disable_custom_outputs: outputs = None @@ -93,7 +119,12 @@ class TfLoaderArgs(BaseArgs): model_type = self.model_args.model_type if model_type == "ckpt": - G_LOGGER.verbose("Loading a TensorFlow checkpoint. Please ensure you are not using the --use-subprocess flag".format(model_file), mode=LogMode.ONCE) + G_LOGGER.verbose( + "Loading a TensorFlow checkpoint. Please ensure you are not using the --use-subprocess flag".format( + model_file + ), + mode=LogMode.ONCE, + ) script.add_import(imports=["GraphFromCkpt"], frm="polygraphy.backend.tf") loader_id = "load_ckpt" loader_str = make_invocable("GraphFromCkpt", model_file, self.ckpt) @@ -103,21 +134,34 @@ class TfLoaderArgs(BaseArgs): loader_str = make_invocable("GraphFromKeras", model_file) elif model_type == "frozen": script.add_import(imports=["GraphFromFrozen"], frm="polygraphy.backend.tf") - G_LOGGER.verbose("Attempting to load as a frozen graph. If this is not correct, please specify --model-type", mode=LogMode.ONCE) + G_LOGGER.verbose( + "Attempting to load as a frozen graph. If this is not correct, please specify --model-type", + mode=LogMode.ONCE, + ) loader_id = "load_frozen" loader_str = make_invocable("GraphFromFrozen", model_file) else: - G_LOGGER.exit("Model type: {:} cannot be imported with TensorFlow.".format(model_type)) + G_LOGGER.critical("Model type: {:} cannot be imported with TensorFlow.".format(model_type)) loader_name = script.add_loader(loader_str, loader_id, suffix=suffix) if self.freeze_graph: script.add_import(imports=["OptimizeGraph"], frm="polygraphy.backend.tf") - loader_name = script.add_loader(make_invocable("OptimizeGraph", loader_name), "optimize_graph", suffix=suffix) + loader_name = script.add_loader( + make_invocable("OptimizeGraph", loader_name), "optimize_graph", suffix=suffix + ) if self.tftrt: script.add_import(imports=["UseTfTrt"], frm="polygraphy.backend.tf") - loader_str = make_invocable("UseTfTrt", loader_name, max_workspace_size=self.trt_config_args.workspace, fp16=self.trt_config_args.fp16, int8=self.trt_config_args.int8, - max_batch_size=self.trt_legacy_args.batch_size, is_dynamic_op=self.dynamic_op, minimum_segment_size=self.minimum_segment_size) + loader_str = make_invocable( + "UseTfTrt", + loader_name, + max_workspace_size=self.trt_config_args.workspace, + fp16=self.trt_config_args.fp16, + int8=self.trt_config_args.int8, + max_batch_size=self.trt_legacy_args.batch_size, + is_dynamic_op=self.dynamic_op, + minimum_segment_size=self.minimum_segment_size, + ) loader_name = script.add_loader(loader_str, "use_tftrt", suffix=suffix) MODIFY_TF = "ModifyGraphOutputs" @@ -131,14 +175,15 @@ class TfLoaderArgs(BaseArgs): engine_dir = self.trt_engine_save_args.path WRITE_TF = "SaveGraph" - write_tf_str = make_invocable(WRITE_TF, loader_name, path=self.save_pb, tensorboard_dir=self.save_tensorboard, engine_dir=engine_dir) + write_tf_str = make_invocable( + WRITE_TF, loader_name, path=self.save_pb, tensorboard_dir=self.save_tensorboard, engine_dir=engine_dir + ) if write_tf_str != make_invocable(WRITE_TF, loader_name): script.add_import(imports=[WRITE_TF], frm="polygraphy.backend.tf") loader_name = script.add_loader(write_tf_str, "save_tf") return loader_name - def load_graph(self): loader = args_util.run_script(self.add_to_script) return loader() diff --git a/tools/Polygraphy/polygraphy/tools/args/tf/runner.py b/tools/Polygraphy/polygraphy/tools/args/tf/runner.py index 56a991a3..8a1aea90 100644 --- a/tools/Polygraphy/polygraphy/tools/args/tf/runner.py +++ b/tools/Polygraphy/polygraphy/tools/args/tf/runner.py @@ -24,8 +24,11 @@ from polygraphy.tools.script import make_invocable class TfRunnerArgs(BaseArgs): def add_to_parser(self, parser): tf_args = parser.add_argument_group("TensorFlow Runner", "Options for TensorFlow Inference") - tf_args.add_argument("--save-timeline", help="[EXPERIMENTAL] Directory to save timeline JSON files for profiling inference (view at chrome://tracing)", default=None) - + tf_args.add_argument( + "--save-timeline", + help="[EXPERIMENTAL] Directory to save timeline JSON files for profiling inference (view at chrome://tracing)", + default=None, + ) def register(self, maker): from polygraphy.tools.args.tf.config import TfConfigArgs @@ -36,16 +39,13 @@ class TfRunnerArgs(BaseArgs): if isinstance(maker, TfConfigArgs): self.tf_config_args = maker - def check_registered(self): assert self.tf_loader_args is not None, "TfLoaderArgs is required!" assert self.tf_config_args is not None, "TfConfigArgs is required!" - def parse(self, args): self.timeline_path = args_util.get(args, "save_timeline") - def add_to_script(self, script): script.add_import(imports=["TfRunner"], frm="polygraphy.backend.tf") @@ -53,6 +53,8 @@ class TfRunnerArgs(BaseArgs): config_name = self.tf_config_args.add_to_script(script) script.add_import(imports=["SessionFromGraph"], frm="polygraphy.backend.tf") - loader_name = script.add_loader(make_invocable("SessionFromGraph", graph_name, config=config_name), "build_tf_session") + loader_name = script.add_loader( + make_invocable("SessionFromGraph", graph_name, config=config_name), "build_tf_session" + ) script.add_runner(make_invocable("TfRunner", loader_name, timeline_path=self.timeline_path)) diff --git a/tools/Polygraphy/polygraphy/tools/args/tf2onnx/loader.py b/tools/Polygraphy/polygraphy/tools/args/tf2onnx/loader.py index 557cd40d..6727e007 100644 --- a/tools/Polygraphy/polygraphy/tools/args/tf2onnx/loader.py +++ b/tools/Polygraphy/polygraphy/tools/args/tf2onnx/loader.py @@ -25,8 +25,12 @@ class Tf2OnnxLoaderArgs(BaseArgs): def add_to_parser(self, parser): tf_onnx_args = parser.add_argument_group("TensorFlow-ONNX Loader", "Options for TensorFlow-ONNX conversion") tf_onnx_args.add_argument("--opset", help="Opset to use when converting to ONNX", default=None, type=int) - tf_onnx_args.add_argument("--no-const-folding", help="Do not fold constants in the TensorFlow graph prior to conversion", action="store_true", default=None) - + tf_onnx_args.add_argument( + "--no-const-folding", + help="Do not fold constants in the TensorFlow graph prior to conversion", + action="store_true", + default=None, + ) def register(self, maker): from polygraphy.tools.args.tf.loader import TfLoaderArgs @@ -34,21 +38,25 @@ class Tf2OnnxLoaderArgs(BaseArgs): if isinstance(maker, TfLoaderArgs): self.tf_loader_args = maker - def check_registered(self): assert self.tf_loader_args is not None, "TfLoaderArgs is required!" - def parse(self, args): self.opset = args_util.get(args, "opset") self.fold_constant = False if args_util.get(args, "no_const_folding") else None - def add_to_script(self, script, suffix=None): - G_LOGGER.verbose("Attempting to load as a TensorFlow model, using TF2ONNX to convert to ONNX. " - "If this is not correct, please specify --model-type", mode=LogMode.ONCE) + G_LOGGER.verbose( + "Attempting to load as a TensorFlow model, using TF2ONNX to convert to ONNX. " + "If this is not correct, please specify --model-type", + mode=LogMode.ONCE, + ) script.add_import(imports=["OnnxFromTfGraph"], frm="polygraphy.backend.onnx") - loader_str = make_invocable("OnnxFromTfGraph", self.tf_loader_args.add_to_script(script, disable_custom_outputs=True, suffix=suffix), - opset=self.opset, fold_constant=self.fold_constant) + loader_str = make_invocable( + "OnnxFromTfGraph", + self.tf_loader_args.add_to_script(script, disable_custom_outputs=True, suffix=suffix), + opset=self.opset, + fold_constant=self.fold_constant, + ) loader_name = script.add_loader(loader_str, "export_onnx_from_tf", suffix=suffix) return loader_name diff --git a/tools/Polygraphy/polygraphy/tools/args/trt/config.py b/tools/Polygraphy/polygraphy/tools/args/trt/config.py index 617abc1a..53ccdf93 100644 --- a/tools/Polygraphy/polygraphy/tools/args/trt/config.py +++ b/tools/Polygraphy/polygraphy/tools/args/trt/config.py @@ -21,8 +21,7 @@ from polygraphy.common import TensorMetadata from polygraphy.logger import G_LOGGER, LogMode from polygraphy.tools.args import util as args_util from polygraphy.tools.args.base import BaseArgs -from polygraphy.tools.script import (assert_identifier, inline, make_invocable, - make_invocable_if_nondefault, safe) +from polygraphy.tools.script import assert_identifier, inline, make_invocable, make_invocable_if_nondefault, safe def parse_profile_shapes(default_shapes, min_args, opt_args, max_args): @@ -37,6 +36,7 @@ def parse_profile_shapes(default_shapes, min_args, opt_args, max_args): A list of profiles with each profile comprised of three dictionaries (min, opt, max) mapping input names to shapes. """ + def get_shapes(lst, idx): nonlocal default_shapes default_shapes = copy.copy(default_shapes) @@ -47,12 +47,16 @@ def parse_profile_shapes(default_shapes, min_args, opt_args, max_args): shapes = {name: util.override_dynamic_shape(shape) for name, (_, shape) in default_shapes.items()} for name, shape in shapes.items(): - if tuple(shapes[name]) != tuple(shape): - G_LOGGER.warning("Input tensor: {:} | For TensorRT profile, overriding shape: {:} to: {:}".format(name, shape, shapes[name]), mode=LogMode.ONCE) + if tuple(default_shapes[name].shape) != tuple(shape): + G_LOGGER.warning( + "Input tensor: {:} | For TensorRT profile, overriding dynamic shape: {:} to: {:}".format( + name, default_shapes[name].shape, shape + ), + mode=LogMode.ONCE, + ) return shapes - num_profiles = max(len(min_args), len(opt_args), len(max_args)) # For cases where input shapes are provided, we have to generate a profile @@ -65,11 +69,15 @@ def parse_profile_shapes(default_shapes, min_args, opt_args, max_args): opt_shapes = get_shapes(opt_args, idx) max_shapes = get_shapes(max_args, idx) if sorted(min_shapes.keys()) != sorted(opt_shapes.keys()): - G_LOGGER.exit("Mismatch in input names between minimum shapes ({:}) and optimum shapes " - "({:})".format(list(min_shapes.keys()), list(opt_shapes.keys()))) + G_LOGGER.critical( + "Mismatch in input names between minimum shapes ({:}) and optimum shapes " + "({:})".format(list(min_shapes.keys()), list(opt_shapes.keys())) + ) elif sorted(opt_shapes.keys()) != sorted(max_shapes.keys()): - G_LOGGER.exit("Mismatch in input names between optimum shapes ({:}) and maximum shapes " - "({:})".format(list(opt_shapes.keys()), list(max_shapes.keys()))) + G_LOGGER.critical( + "Mismatch in input names between optimum shapes ({:}) and maximum shapes " + "({:})".format(list(opt_shapes.keys()), list(max_shapes.keys())) + ) profiles.append((min_shapes, opt_shapes, max_shapes)) return profiles @@ -77,84 +85,177 @@ def parse_profile_shapes(default_shapes, min_args, opt_args, max_args): @mod.export() class TrtConfigArgs(BaseArgs): - def __init__(self, force_strict_types=None): + def __init__(self, strict_types_default=None): super().__init__() self.model_args = None self.data_loader_args = None - self._force_strict_types = force_strict_types - + self._strict_types_default = strict_types_default def add_to_parser(self, parser): - trt_config_args = parser.add_argument_group("TensorRT Builder Configuration", "Options for TensorRT Builder Configuration") - trt_config_args.add_argument("--trt-min-shapes", action='append', help="The minimum shapes the optimization profile(s) will support. " - "Specify this option once for each profile. If not provided, inference-time input shapes are used. " - "Format: --trt-min-shapes :[D0,D1,..,DN] .. :[D0,D1,..,DN]", nargs="+", default=[]) - trt_config_args.add_argument("--trt-opt-shapes", action='append', help="The shapes for which the optimization profile(s) will be most performant. " - "Specify this option once for each profile. If not provided, inference-time input shapes are used. " - "Format: --trt-opt-shapes :[D0,D1,..,DN] .. :[D0,D1,..,DN]", nargs="+", default=[]) - trt_config_args.add_argument("--trt-max-shapes", action='append', help="The maximum shapes the optimization profile(s) will support. " - "Specify this option once for each profile. If not provided, inference-time input shapes are used. " - "Format: --trt-max-shapes :[D0,D1,..,DN] .. :[D0,D1,..,DN]", nargs="+", default=[]) + trt_config_args = parser.add_argument_group( + "TensorRT Builder Configuration", "Options for TensorRT Builder Configuration" + ) + trt_config_args.add_argument( + "--trt-min-shapes", + action="append", + help="The minimum shapes the optimization profile(s) will support. " + "Specify this option once for each profile. If not provided, inference-time input shapes are used. " + "Format: --trt-min-shapes :[D0,D1,..,DN] .. :[D0,D1,..,DN]", + nargs="+", + default=[], + ) + trt_config_args.add_argument( + "--trt-opt-shapes", + action="append", + help="The shapes for which the optimization profile(s) will be most performant. " + "Specify this option once for each profile. If not provided, inference-time input shapes are used. " + "Format: --trt-opt-shapes :[D0,D1,..,DN] .. :[D0,D1,..,DN]", + nargs="+", + default=[], + ) + trt_config_args.add_argument( + "--trt-max-shapes", + action="append", + help="The maximum shapes the optimization profile(s) will support. " + "Specify this option once for each profile. If not provided, inference-time input shapes are used. " + "Format: --trt-max-shapes :[D0,D1,..,DN] .. :[D0,D1,..,DN]", + nargs="+", + default=[], + ) - trt_config_args.add_argument("--tf32", help="Enable tf32 precision in TensorRT", action="store_true", default=None) - trt_config_args.add_argument("--fp16", help="Enable fp16 precision in TensorRT", action="store_true", default=None) - trt_config_args.add_argument("--int8", help="Enable int8 precision in TensorRT. " - "If no calibration cache is provided, this option will cause TensorRT to run int8 calibration " - "using the Polygraphy data loader to provide calibration data. ", action="store_true", default=None) - if not self._force_strict_types: - trt_config_args.add_argument("--strict-types", help="Enable strict types in TensorRT, forcing it to choose tactics based on the " - "layer precision set, even if another precision is faster.", action="store_true", - default=None) - trt_config_args.add_argument("--sparse-weights", help="Enable optimizations for sparse weights in TensorRT", action="store_true", default=None) + trt_config_args.add_argument( + "--tf32", help="Enable tf32 precision in TensorRT", action="store_true", default=None + ) + trt_config_args.add_argument( + "--fp16", help="Enable fp16 precision in TensorRT", action="store_true", default=None + ) + trt_config_args.add_argument( + "--int8", + help="Enable int8 precision in TensorRT. " + "If no calibration cache is provided, this option will cause TensorRT to run int8 calibration " + "using the Polygraphy data loader to provide calibration data. ", + action="store_true", + default=None, + ) + if self._strict_types_default: + trt_config_args.add_argument( + "--no-strict-types", + help="Disables strict types in TensorRT, allowing it to choose tactics outside the " + "layer precision set.", + action="store_false", + default=True, + dest="strict_types", + ) + else: + trt_config_args.add_argument( + "--strict-types", + help="Enable strict types in TensorRT, forcing it to choose tactics based on the " + "layer precision set, even if another precision is faster.", + action="store_true", + default=None, + dest="strict_types", + ) + + trt_config_args.add_argument( + "--sparse-weights", + help="Enable optimizations for sparse weights in TensorRT", + action="store_true", + default=None, + ) # Workspace uses float to enable scientific notation (e.g. 1e9) - trt_config_args.add_argument("--workspace", metavar="BYTES", help="Memory in bytes to allocate for the TensorRT builder's workspace", type=float, default=None) - trt_config_args.add_argument("--calibration-cache", help="Path to load/save a calibration cache. " - "Used to store calibration scales to speed up the process of int8 calibration. " - "If the provided path does not yet exist, int8 calibration scales will be calculated and written to it during engine building. " - "If the provided path does exist, it will be read and int8 calibration will be skipped during engine building. ", - default=None) - trt_config_args.add_argument("--calib-base-cls", "--calibration-base-class", dest="calibration_base_class", - help="The name of the calibration base class to use. For example, 'IInt8MinMaxCalibrator'. ", - default=None) - trt_config_args.add_argument("--quantile", type=float, - help="The quantile to use for IInt8LegacyCalibrator. Has no effect for other calibrator types.", - default=None) - trt_config_args.add_argument("--regression-cutoff", type=float, - help="The regression cutoff to use for IInt8LegacyCalibrator. Has no effect for other calibrator types.", - default=None) + trt_config_args.add_argument( + "--workspace", + metavar="BYTES", + help="Memory in bytes to allocate for the TensorRT builder's workspace", + type=float, + default=None, + ) + trt_config_args.add_argument( + "--calibration-cache", + help="Path to load/save a calibration cache. " + "Used to store calibration scales to speed up the process of int8 calibration. " + "If the provided path does not yet exist, int8 calibration scales will be calculated and written to it during engine building. " + "If the provided path does exist, it will be read and int8 calibration will be skipped during engine building. ", + default=None, + ) + trt_config_args.add_argument( + "--calib-base-cls", + "--calibration-base-class", + dest="calibration_base_class", + help="The name of the calibration base class to use. For example, 'IInt8MinMaxCalibrator'. ", + default=None, + ) + trt_config_args.add_argument( + "--quantile", + type=float, + help="The quantile to use for IInt8LegacyCalibrator. Has no effect for other calibrator types.", + default=None, + ) + trt_config_args.add_argument( + "--regression-cutoff", + type=float, + help="The regression cutoff to use for IInt8LegacyCalibrator. Has no effect for other calibrator types.", + default=None, + ) - trt_config_args.add_argument("--timing-cache", help="Path to load/save tactic timing cache. " - "Used to cache tactic timing information to speed up the engine building process. " - "Existing caches will be appended to with any new timing information gathered. ", - default=None) + trt_config_args.add_argument( + "--timing-cache", + help="Path to load/save tactic timing cache. " + "Used to cache tactic timing information to speed up the engine building process. " + "Existing caches will be appended to with any new timing information gathered. ", + default=None, + ) replay = trt_config_args.add_mutually_exclusive_group() - replay.add_argument("--tactic-replay", help="[DEPRECATED - use --load/save-tactics] Path to load/save a tactic replay file. " - "Used to record and replay tactics selected by TensorRT to provide deterministic engine builds. " - "If the provided path does not yet exist, tactics will be recorded and written to it. " - "If the provided path does exist, it will be read and used to replay previously recorded tactics. ", - default=None) - replay.add_argument("--save-tactics", help="Path to save a tactic replay file. " - "Tactics selected by TensorRT will be recorded and stored at this location. ", - default=None) - replay.add_argument("--load-tactics", help="Path to load a tactic replay file. " - "The tactics specified in the file will be used to override TensorRT's default selections. ", - default=None) + replay.add_argument( + "--tactic-replay", + help="[DEPRECATED - use --load/save-tactics] Path to load/save a tactic replay file. " + "Used to record and replay tactics selected by TensorRT to provide deterministic engine builds. " + "If the provided path does not yet exist, tactics will be recorded and written to it. " + "If the provided path does exist, it will be read and used to replay previously recorded tactics. ", + default=None, + ) + replay.add_argument( + "--save-tactics", + help="Path to save a tactic replay file. " + "Tactics selected by TensorRT will be recorded and stored at this location. ", + default=None, + ) + replay.add_argument( + "--load-tactics", + help="Path to load a tactic replay file. " + "The tactics specified in the file will be used to override TensorRT's default selections. ", + default=None, + ) - trt_config_args.add_argument("--tactic-sources", help="Tactic sources to enable. This controls which libraries " - "(e.g. cudnn, cublas, etc.) TensorRT is allowed to load tactics from. " - "Values come from the names of the values in the trt.TacticSource enum, and are case-insensitive. " - "If no arguments are provided, e.g. '--tactic-sources', then all tactic sources are disabled.", - nargs="*", default=None) - - trt_config_args.add_argument("--trt-config-script", help="Path to a Python script that defines a function that creates a " - "TensorRT IBuilderConfig. The function should take a builder and network as parameters and return a " - "TensorRT builder configuration. When this option is specified, all other config arguments are ignored. ", - default=None) - trt_config_args.add_argument("--trt-config-func-name", help="When using a trt-config-script, this specifies the name of the function " - "that creates the config. Defaults to `load_config`. ", default="load_config") + trt_config_args.add_argument( + "--tactic-sources", + help="Tactic sources to enable. This controls which libraries " + "(e.g. cudnn, cublas, etc.) TensorRT is allowed to load tactics from. " + "Values come from the names of the values in the trt.TacticSource enum, and are case-insensitive. " + "If no arguments are provided, e.g. '--tactic-sources', then all tactic sources are disabled.", + nargs="*", + default=None, + ) + trt_config_args.add_argument( + "--trt-config-script", + help="Path to a Python script that defines a function that creates a " + "TensorRT IBuilderConfig. The function should take a builder and network as parameters and return a " + "TensorRT builder configuration. When this option is specified, all other config arguments are ignored. ", + default=None, + ) + trt_config_args.add_argument( + "--trt-config-func-name", + help="When using a trt-config-script, this specifies the name of the function " + "that creates the config. Defaults to `load_config`. ", + default="load_config", + ) + trt_config_args.add_argument( + "--trt-safety-restricted", help="Enable safety scope checking in TensorRT", action="store_true", default=None, + dest="restricted", + ) def register(self, maker): from polygraphy.tools.args.data_loader import DataLoaderArgs @@ -165,7 +266,6 @@ class TrtConfigArgs(BaseArgs): if isinstance(maker, DataLoaderArgs): self.data_loader_args = maker - def parse(self, args): trt_min_shapes = util.default(args_util.get(args, "trt_min_shapes"), []) trt_max_shapes = util.default(args_util.get(args, "trt_max_shapes"), []) @@ -184,7 +284,8 @@ class TrtConfigArgs(BaseArgs): self.tf32 = args_util.get(args, "tf32") self.fp16 = args_util.get(args, "fp16") self.int8 = args_util.get(args, "int8") - self.strict_types = args_util.get(args, "strict_types") if not self._force_strict_types else True + self.strict_types = args_util.get(args, "strict_types") + self.restricted = args_util.get(args, "restricted") self.calibration_cache = args_util.get(args, "calibration_cache") calib_base = args_util.get(args, "calibration_base_class") @@ -222,13 +323,14 @@ class TrtConfigArgs(BaseArgs): self.trt_config_script = args_util.get(args, "trt_config_script") self.trt_config_func_name = args_util.get(args, "trt_config_func_name") - def add_trt_config_loader(self, script): profiles = [] for (min_shape, opt_shape, max_shape) in self.profile_dicts: profile_str = "Profile()" for name in min_shape.keys(): - profile_str += safe(".add({:}, min={:}, opt={:}, max={:})", name, min_shape[name], opt_shape[name], max_shape[name]).unwrap() + profile_str += safe( + ".add({:}, min={:}, opt={:}, max={:})", name, min_shape[name], opt_shape[name], max_shape[name] + ).unwrap() profiles.append(profile_str) if profiles: script.add_import(imports=["Profile"], frm="polygraphy.backend.trt") @@ -239,19 +341,26 @@ class TrtConfigArgs(BaseArgs): calibrator = None if any(arg is not None for arg in [self.calibration_cache, self.calibration_base_class]) and not self.int8: - G_LOGGER.warning("Some int8 calibrator options were set, but int8 precision is not enabled. " - "Calibration options will be ignored. Please set --int8 to enable calibration. ") + G_LOGGER.warning( + "Some int8 calibrator options were set, but int8 precision is not enabled. " + "Calibration options will be ignored. Please set --int8 to enable calibration. " + ) - if self.int8 and self.data_loader_args is not None: # We cannot do calibration if there is no data loader. + if self.int8 and self.data_loader_args is not None: # We cannot do calibration if there is no data loader. script.add_import(imports=["Calibrator"], frm="polygraphy.backend.trt") script.add_import(imports=["DataLoader"], frm="polygraphy.comparator") - data_loader_name = self.data_loader_args.add_to_script(script) + data_loader_name = self.data_loader_args.add_data_loader(script) if self.calibration_base_class: script.add_import(imports=["tensorrt as trt"]) - calibrator = make_invocable("Calibrator", data_loader=data_loader_name if data_loader_name else inline(safe("DataLoader()")), - cache=self.calibration_cache, BaseClass=self.calibration_base_class, - quantile=self.quantile, regression_cutoff=self.regression_cutoff) + calibrator = make_invocable( + "Calibrator", + data_loader=data_loader_name if data_loader_name else inline(safe("DataLoader()")), + cache=self.calibration_cache, + BaseClass=self.calibration_base_class, + quantile=self.quantile, + regression_cutoff=self.regression_cutoff, + ) algo_selector = None if self.load_tactics is not None: @@ -266,14 +375,27 @@ class TrtConfigArgs(BaseArgs): if self.trt_config_script is not None: script.add_import(imports=["InvokeFromScript"], frm="polygraphy.backend.common") - config_loader_str = make_invocable("InvokeFromScript", self.trt_config_script, name=self.trt_config_func_name) + config_loader_str = make_invocable( + "InvokeFromScript", self.trt_config_script, name=self.trt_config_func_name + ) else: - config_loader_str = make_invocable_if_nondefault("CreateTrtConfig", max_workspace_size=self.workspace, tf32=self.tf32, - fp16=self.fp16, int8=self.int8, strict_types=self.strict_types, - profiles=profile_name, calibrator=calibrator, - load_timing_cache=(self.timing_cache if self.timing_cache and os.path.exists(self.timing_cache) else None), - algorithm_selector=algo_selector, - sparse_weights=self.sparse_weights, tactic_sources=self.tactic_sources) + config_loader_str = make_invocable_if_nondefault( + "CreateTrtConfig", + max_workspace_size=self.workspace, + tf32=self.tf32, + fp16=self.fp16, + int8=self.int8, + strict_types=self.strict_types, + restricted=self.restricted, + profiles=profile_name, + calibrator=calibrator, + load_timing_cache=( + self.timing_cache if self.timing_cache and os.path.exists(self.timing_cache) else None + ), + algorithm_selector=algo_selector, + sparse_weights=self.sparse_weights, + tactic_sources=self.tactic_sources, + ) if config_loader_str is not None: script.add_import(imports=["CreateConfig as CreateTrtConfig"], frm="polygraphy.backend.trt") @@ -283,8 +405,8 @@ class TrtConfigArgs(BaseArgs): config_loader_name = None return config_loader_name - def create_config(self, builder, network): from polygraphy.backend.trt import CreateConfig + loader = util.default(args_util.run_script(self.add_trt_config_loader), CreateConfig()) return loader(builder, network) diff --git a/tools/Polygraphy/polygraphy/tools/args/trt/loader.py b/tools/Polygraphy/polygraphy/tools/args/trt/loader.py index 3e1f1cd2..f2fd875c 100644 --- a/tools/Polygraphy/polygraphy/tools/args/trt/loader.py +++ b/tools/Polygraphy/polygraphy/tools/args/trt/loader.py @@ -26,11 +26,9 @@ class TrtPluginLoaderArgs(BaseArgs): trt_args = parser.add_argument_group("TensorRT Plugin Loader", "Options for TensorRT Plugin Loader") trt_args.add_argument("--plugins", help="Path(s) of plugin libraries to load", nargs="+", default=None) - def parse(self, args): self.plugins = args_util.get(args, "plugins") - # If plugins are present, wrap the provided loader/object with LoadPlugins def wrap_if_plugins(self, script, loader_name): if self.plugins: @@ -48,18 +46,31 @@ class TrtNetworkLoaderArgs(BaseArgs): self._outputs = outputs - def add_to_parser(self, parser): trt_args = parser.add_argument_group("TensorRT Network Loader", "Options for TensorRT Network Loader") - trt_args.add_argument("--explicit-precision", help="Enable explicit precision mode", action="store_true", default=None) + trt_args.add_argument( + "--explicit-precision", help="Enable explicit precision mode", action="store_true", default=None + ) if self._outputs: - trt_args.add_argument("--trt-outputs", help="Name(s) of TensorRT output(s). " - "Using '--trt-outputs mark all' indicates that all tensors should be used as outputs", nargs="+", default=None) - trt_args.add_argument("--trt-exclude-outputs", help="[EXPERIMENTAL] Name(s) of TensorRT output(s) to unmark as outputs.", - nargs="+", default=None) - trt_args.add_argument("--trt-network-func-name", help="When using a trt-network-script instead of other model types, this specifies the name " - "of the function that loads the network. Defaults to `load_network`.", default="load_network") - + trt_args.add_argument( + "--trt-outputs", + help="Name(s) of TensorRT output(s). " + "Using '--trt-outputs mark all' indicates that all tensors should be used as outputs", + nargs="+", + default=None, + ) + trt_args.add_argument( + "--trt-exclude-outputs", + help="[EXPERIMENTAL] Name(s) of TensorRT output(s) to unmark as outputs.", + nargs="+", + default=None, + ) + trt_args.add_argument( + "--trt-network-func-name", + help="When using a trt-network-script instead of other model types, this specifies the name " + "of the function that loads the network. Defaults to `load_network`.", + default="load_network", + ) def register(self, maker): from polygraphy.tools.args.model import ModelArgs @@ -75,19 +86,16 @@ class TrtNetworkLoaderArgs(BaseArgs): if isinstance(maker, TrtPluginLoaderArgs): self.trt_plugin_args = maker - def check_registered(self): assert self.model_args is not None, "ModelArgs is required!" assert self.trt_plugin_args is not None, "TrtPluginLoaderArgs is required!" - def parse(self, args): self.outputs = args_util.get_outputs(args, "trt_outputs") self.explicit_precision = args_util.get(args, "explicit_precision") self.exclude_outputs = args_util.get(args, "trt_exclude_outputs") self.trt_network_func_name = args_util.get(args, "trt_network_func_name") - def add_trt_network_loader(self, script): model_file = self.model_args.model_file model_type = self.model_args.model_type @@ -98,28 +106,41 @@ class TrtNetworkLoaderArgs(BaseArgs): loader_str = make_invocable("InvokeFromScript", model_file, name=self.trt_network_func_name) loader_name = script.add_loader(loader_str, "load_network") # When loading from ONNX, we need to disable custom outputs since TRT requires dtypes on outputs, which our marking function doesn't guarantee. - elif self.onnx_loader_args is not None and self.onnx_loader_args.should_use_onnx_loader(disable_custom_outputs=True): + elif self.onnx_loader_args is not None and self.onnx_loader_args.should_use_onnx_loader( + disable_custom_outputs=True + ): script.add_import(imports=["NetworkFromOnnxBytes"], frm="polygraphy.backend.trt") onnx_loader = self.onnx_loader_args.add_serialized_onnx_loader(script, disable_custom_outputs=True) - loader_str = make_invocable("NetworkFromOnnxBytes", self.trt_plugin_args.wrap_if_plugins(script, onnx_loader), explicit_precision=self.explicit_precision) + loader_str = make_invocable( + "NetworkFromOnnxBytes", + self.trt_plugin_args.wrap_if_plugins(script, onnx_loader), + explicit_precision=self.explicit_precision, + ) loader_name = script.add_loader(loader_str, "parse_network_from_onnx") else: script.add_import(imports=["NetworkFromOnnxPath"], frm="polygraphy.backend.trt") - loader_str = make_invocable("NetworkFromOnnxPath", self.trt_plugin_args.wrap_if_plugins(script, model_file), explicit_precision=self.explicit_precision) + loader_str = make_invocable( + "NetworkFromOnnxPath", + self.trt_plugin_args.wrap_if_plugins(script, model_file), + explicit_precision=self.explicit_precision, + ) loader_name = script.add_loader(loader_str, "parse_network_from_onnx") MODIFY_NETWORK = "ModifyNetworkOutputs" - modify_network_str = make_invocable(MODIFY_NETWORK, loader_name, outputs=outputs, exclude_outputs=self.exclude_outputs) - if modify_network_str != make_invocable(MODIFY_NETWORK, loader_name): + modify_network_str = make_invocable( + MODIFY_NETWORK, loader_name, outputs=outputs, exclude_outputs=self.exclude_outputs + ) + if str(modify_network_str) != str(make_invocable(MODIFY_NETWORK, loader_name)): script.add_import(imports=[MODIFY_NETWORK], frm="polygraphy.backend.trt") loader_name = script.add_loader(modify_network_str, "modify_network") return loader_name + def get_network_loader(self): + return args_util.run_script(self.add_trt_network_loader) def load_network(self): - loader = args_util.run_script(self.add_trt_network_loader) - return loader() + return self.get_network_loader()() @mod.export() @@ -129,19 +150,20 @@ class TrtEngineSaveArgs(BaseArgs): self._output = output self._short_opt = short_opt - def add_to_parser(self, parser): if self._output: - self.group = parser.add_argument_group("TensorRT Engine Save Options", "Options for saving TensorRT engines") + self.group = parser.add_argument_group( + "TensorRT Engine Save Options", "Options for saving TensorRT engines" + ) flag = "--{:}".format(self._output) short = self._short_opt or flag - self.group.add_argument(short, flag, help="Path to save the TensorRT Engine", dest="save_engine", default=None) - + self.group.add_argument( + short, flag, help="Path to save the TensorRT Engine", dest="save_engine", default=None + ) def parse(self, args): self.path = args_util.get(args, "save_engine") - def add_save_engine(self, script, loader_name): if self.path is None: return loader_name @@ -149,7 +171,6 @@ class TrtEngineSaveArgs(BaseArgs): script.add_import(imports=["SaveEngine"], frm="polygraphy.backend.trt") return script.add_loader(make_invocable("SaveEngine", loader_name, path=self.path), "save_engine") - def save_engine(self, engine, path=None): with util.TempAttrChange(self, "path", path): loader = args_util.run_script(self.add_save_engine, engine) @@ -163,7 +184,6 @@ class TrtEngineLoaderArgs(BaseArgs): self.trt_engine_save_args = None self._save = save - def register(self, maker): from polygraphy.tools.args.model import ModelArgs from polygraphy.tools.args.trt.config import TrtConfigArgs @@ -179,26 +199,27 @@ class TrtEngineLoaderArgs(BaseArgs): if self._save and isinstance(maker, TrtEngineSaveArgs): self.trt_engine_save_args = maker - def check_registered(self): assert self.model_args is not None, "ModelArgs is required!" assert self.trt_plugin_args is not None, "TrtPluginLoaderArgs is required!" assert not self._save or self.trt_engine_save_args is not None, "TrtEngineSaveArgs is required to use save=True" - def parse(self, args): self.plugins = args_util.get(args, "plugins") - def add_trt_serialized_engine_loader(self, script): assert self.model_args is not None, "ModelArgs is required for engine deserialization!" script.add_import(imports=["EngineFromBytes"], frm="polygraphy.backend.trt") script.add_import(imports=["BytesFromPath"], frm="polygraphy.backend.common") - load_engine = script.add_loader(make_invocable("BytesFromPath", self.model_args.model_file), "load_engine_bytes") - return script.add_loader(make_invocable("EngineFromBytes", self.trt_plugin_args.wrap_if_plugins(script, load_engine)), "deserialize_engine") - + load_engine = script.add_loader( + make_invocable("BytesFromPath", self.model_args.model_file), "load_engine_bytes" + ) + return script.add_loader( + make_invocable("EngineFromBytes", self.trt_plugin_args.wrap_if_plugins(script, load_engine)), + "deserialize_engine", + ) def add_trt_build_engine_loader(self, script, network_name=None): if network_name: @@ -211,20 +232,22 @@ class TrtEngineLoaderArgs(BaseArgs): script.add_import(imports=["EngineFromNetwork"], frm="polygraphy.backend.trt") config_loader_name = self.trt_config_args.add_trt_config_loader(script) - loader_str = make_invocable("EngineFromNetwork", self.trt_plugin_args.wrap_if_plugins(script, network_loader_name), - config=config_loader_name, save_timing_cache=self.trt_config_args.timing_cache) + loader_str = make_invocable( + "EngineFromNetwork", + self.trt_plugin_args.wrap_if_plugins(script, network_loader_name), + config=config_loader_name, + save_timing_cache=self.trt_config_args.timing_cache, + ) loader_name = script.add_loader(loader_str, "build_engine") if self.trt_engine_save_args is not None: loader_name = self.trt_engine_save_args.add_save_engine(script, loader_name) return loader_name - def build_engine(self, network=None): loader = args_util.run_script(self.add_trt_build_engine_loader, network) return loader() - def load_serialized_engine(self): loader = args_util.run_script(self.add_trt_serialized_engine_loader) return loader() diff --git a/tools/Polygraphy/polygraphy/tools/args/trt/runner.py b/tools/Polygraphy/polygraphy/tools/args/trt/runner.py index 2531c98f..4a3c93f1 100644 --- a/tools/Polygraphy/polygraphy/tools/args/trt/runner.py +++ b/tools/Polygraphy/polygraphy/tools/args/trt/runner.py @@ -29,12 +29,10 @@ class TrtRunnerArgs(BaseArgs): elif isinstance(maker, TrtEngineLoaderArgs): self.trt_engine_loader_args = maker - def check_registered(self): assert self.model_args is not None, "ModelArgs is required!" assert self.trt_engine_loader_args is not None, "TrtEngineLoaderArgs is required!" - def add_to_script(self, script): script.add_import(imports=["TrtRunner"], frm="polygraphy.backend.trt") diff --git a/tools/Polygraphy/polygraphy/tools/args/trt_legacy.py b/tools/Polygraphy/polygraphy/tools/args/trt_legacy.py index 189e444b..0d440b82 100644 --- a/tools/Polygraphy/polygraphy/tools/args/trt_legacy.py +++ b/tools/Polygraphy/polygraphy/tools/args/trt_legacy.py @@ -23,21 +23,36 @@ from polygraphy.tools.script import make_invocable @mod.export() class TrtLegacyArgs(BaseArgs): def add_to_parser(self, parser): - trt_legacy_args = parser.add_argument_group("TensorRT Legacy", "[DEPRECATED] Options for TensorRT Legacy. Reuses TensorRT options, but does not support int8 mode, or dynamic shapes") - trt_legacy_args.add_argument("-p", "--preprocessor", help="The preprocessor to use for the UFF converter", default=None) + trt_legacy_args = parser.add_argument_group( + "TensorRT Legacy", + "[DEPRECATED] Options for TensorRT Legacy. Reuses TensorRT options, but does not support int8 mode, or dynamic shapes", + ) + trt_legacy_args.add_argument( + "-p", "--preprocessor", help="The preprocessor to use for the UFF converter", default=None + ) trt_legacy_args.add_argument("--uff-order", help="The order of the input", default=None) - trt_legacy_args.add_argument("--batch-size", metavar="SIZE", help="The batch size to use in TensorRT when it cannot be automatically determined", type=int, default=None) - trt_legacy_args.add_argument("--model", help="Model file for Caffe models. The deploy file should be provided as the model_file positional argument", dest="caffe_model") - trt_legacy_args.add_argument("--save-uff", help="Save intermediate UFF files", action="store_true", default=None) - + trt_legacy_args.add_argument( + "--batch-size", + metavar="SIZE", + help="The batch size to use in TensorRT when it cannot be automatically determined", + type=int, + default=None, + ) + trt_legacy_args.add_argument( + "--model", + help="Model file for Caffe models. The deploy file should be provided as the model_file positional argument", + dest="caffe_model", + ) + trt_legacy_args.add_argument( + "--save-uff", help="Save intermediate UFF files", action="store_true", default=None + ) def register(self, maker): from polygraphy.tools.args.model import ModelArgs from polygraphy.tools.args.onnx.loader import OnnxLoaderArgs from polygraphy.tools.args.tf.loader import TfLoaderArgs from polygraphy.tools.args.trt.config import TrtConfigArgs - from polygraphy.tools.args.trt.loader import (TrtEngineLoaderArgs, - TrtEngineSaveArgs) + from polygraphy.tools.args.trt.loader import TrtEngineLoaderArgs, TrtEngineSaveArgs from polygraphy.tools.args.trt.runner import TrtRunnerArgs if isinstance(maker, OnnxLoaderArgs): @@ -55,12 +70,10 @@ class TrtLegacyArgs(BaseArgs): if isinstance(maker, TrtRunnerArgs): self.trt_runner_args = maker - def check_registered(self): assert self.model_args is not None, "ModelArgs is required!" assert self.trt_engine_loader_args is not None, "TrtEngineLoaderArgs is required!" - def parse(self, args): self.trt_outputs = args_util.get(args, "trt_outputs") self.caffe_model = args_util.get(args, "caffe_model") @@ -69,37 +82,68 @@ class TrtLegacyArgs(BaseArgs): self.uff_order = args_util.get(args, "uff_order") self.preprocessor = args_util.get(args, "preprocessor") - def add_to_script(self, script): script.add_import(imports=["TrtLegacyRunner"], frm="polygraphy.backend.trt_legacy") G_LOGGER.warning("Legacy TensorRT runner only supports implicit batch TensorFlow/UFF, ONNX, and Caffe models") + load_engine = self.model_args.model_file if self.model_args.model_type == "engine" else None + + loader_name = None if self.model_args.model_type == "onnx": script.add_import(imports=["ParseNetworkFromOnnxLegacy"], frm="polygraphy.backend.trt_legacy") onnx_loader = self.onnx_loader_args.add_onnx_loader(script, disable_custom_outputs=True) - loader_name = script.add_loader(make_invocable("ParseNetworkFromOnnxLegacy", onnx_loader), "parse_network_from_onnx_legacy") + loader_name = script.add_loader( + make_invocable("ParseNetworkFromOnnxLegacy", onnx_loader), "parse_network_from_onnx_legacy" + ) elif self.model_args.model_type == "caffe": script.add_import(imports=["LoadNetworkFromCaffe"], frm="polygraphy.backend.trt_legacy") - loader_name = script.add_loader(make_invocable("LoadNetworkFromCaffe", self.model_args.model_file, self.caffe_model, - self.trt_outputs, self.batch_size), "parse_network_from_caffe") - else: + loader_name = script.add_loader( + make_invocable( + "LoadNetworkFromCaffe", + self.model_args.model_file, + self.caffe_model, + self.trt_outputs, + self.batch_size, + ), + "parse_network_from_caffe", + ) + elif load_engine is None: script.add_import(imports=["LoadNetworkFromUff"], frm="polygraphy.backend.trt_legacy") if self.model_args.model_type == "uff": script.add_import(imports=["LoadUffFile"], frm="polygraphy.backend.trt_legacy") shapes = {name: shape for name, (_, shape) in self.model_args.input_shapes.items()} - loader_name = script.add_loader(make_invocable("LoadUffFile", self.model_args.model_file, util.default(shapes, {}), self.trt_outputs), "load_uff_file") + loader_name = script.add_loader( + make_invocable( + "LoadUffFile", self.model_args.model_file, util.default(shapes, {}), self.trt_outputs + ), + "load_uff_file", + ) else: script.add_import(imports=["ConvertToUff"], frm="polygraphy.backend.trt_legacy") - loader_name = script.add_loader(make_invocable("ConvertToUff", self.tf_loader_args.add_to_script(script), - save_uff=self.save_uff, preprocessor=self.preprocessor), "convert_to_uff") - loader_name = script.add_loader(make_invocable("LoadNetworkFromUff", loader_name, uff_order=self.uff_order), "uff_network_loader") - - - runner_str = make_invocable("TrtLegacyRunner", - loader_name, self.trt_config_args.workspace, self.batch_size, fp16=self.trt_config_args.fp16, tf32=self.trt_config_args.tf32, - load_engine=self.model_args.model_file if self.model_args.model_type == "engine" else None, - save_engine=self.trt_engine_save_args.path, layerwise=self.trt_outputs==constants.MARK_ALL, - plugins=self.trt_engine_loader_args.plugins) + loader_name = script.add_loader( + make_invocable( + "ConvertToUff", + self.tf_loader_args.add_to_script(script), + save_uff=self.save_uff, + preprocessor=self.preprocessor, + ), + "convert_to_uff", + ) + loader_name = script.add_loader( + make_invocable("LoadNetworkFromUff", loader_name, uff_order=self.uff_order), "uff_network_loader" + ) + runner_str = make_invocable( + "TrtLegacyRunner", + network_loader=loader_name, + max_workspace_size=self.trt_config_args.workspace, + max_batch_size=self.batch_size, + fp16=self.trt_config_args.fp16, + tf32=self.trt_config_args.tf32, + load_engine=load_engine, + save_engine=self.trt_engine_save_args.path, + layerwise=self.trt_outputs == constants.MARK_ALL, + plugins=self.trt_engine_loader_args.plugins, + ) script.add_runner(runner_str) diff --git a/tools/Polygraphy/polygraphy/tools/args/util/util.py b/tools/Polygraphy/polygraphy/tools/args/util/util.py index 618a5501..0127c30b 100644 --- a/tools/Polygraphy/polygraphy/tools/args/util/util.py +++ b/tools/Polygraphy/polygraphy/tools/args/util/util.py @@ -40,12 +40,12 @@ def cast(val): return [cast(elem) for elem in val.strip("[]").split(",")] try: - return int(val) # This fails for float strings like '0.0' + return int(val) # This fails for float strings like '0.0' except: pass try: - return float(val) # This fails for non-numerical strings like 'isildur' + return float(val) # This fails for non-numerical strings like 'isildur' except: pass return val.strip("\"'") @@ -146,8 +146,10 @@ def np_type_from_str(dt_str): try: return {np.dtype(dtype).name: np.dtype(dtype) for dtype in np.sctypeDict.values()}[dt_str] except KeyError: - G_LOGGER.error("Could not understand data type: {:}. Did you forget to specify a data type? " - "Please use one of: {:} or `auto`.".format(dt_str, np_types())) + G_LOGGER.error( + "Could not understand data type: {:}. Did you forget to specify a data type? " + "Please use one of: {:} or `auto`.".format(dt_str, np_types()) + ) raise @@ -172,7 +174,6 @@ def parse_dict_with_default(arg_lst, cast_to=None, sep=None): """ sep = util.default(sep, ":") - if arg_lst is None: return @@ -186,7 +187,11 @@ def parse_dict_with_default(arg_lst, cast_to=None, sep=None): return arg_map -@mod.deprecate(remove_in="0.35.0", use_instead=": as a separator and write shapes in the form [dim0,...,dimN]", name="Using , as a separator") +@mod.deprecate( + remove_in="0.35.0", + use_instead=": as a separator and write shapes in the form [dim0,...,dimN]", + name="Using , as a separator", +) def parse_meta_legacy(meta_args, includes_shape=True, includes_dtype=True): """ Parses a list of tensor metadata arguments of the form ",," @@ -210,21 +215,22 @@ def parse_meta_legacy(meta_args, includes_shape=True, includes_dtype=True): nonlocal tensor_meta_arg tensor_meta_arg, _, val = tensor_meta_arg.rpartition(SEP) if not tensor_meta_arg: - G_LOGGER.exit("Could not parse {:} from argument: {:}. Is it separated by a comma " - "(,) from the tensor name?".format(name, orig_tensor_meta_arg)) + G_LOGGER.critical( + "Could not parse {:} from argument: {:}. Is it separated by a comma " + "(,) from the tensor name?".format(name, orig_tensor_meta_arg) + ) if val.lower() == "auto": val = None return val - def parse_dtype(dtype): if dtype is not None: dtype = np_type_from_str(dtype) return dtype - def parse_shape(shape): if shape is not None: + def parse_shape_dim(buf): try: buf = int(buf) @@ -232,13 +238,12 @@ def parse_meta_legacy(meta_args, includes_shape=True, includes_dtype=True): pass return buf - parsed_shape = [] # Allow for quoted strings in shape dimensions in_quotes = False buf = "" for char in shape.lower(): - if char in ["\"", "'"]: + if char in ['"', "'"]: in_quotes = not in_quotes elif not in_quotes and char == SHAPE_SEP: parsed_shape.append(parse_shape_dim(buf)) @@ -251,7 +256,6 @@ def parse_meta_legacy(meta_args, includes_shape=True, includes_dtype=True): shape = tuple(parsed_shape) return shape - name = None dtype = None shape = None diff --git a/tools/Polygraphy/polygraphy/tools/base/tool.py b/tools/Polygraphy/polygraphy/tools/base/tool.py index 12c848c3..78826f00 100644 --- a/tools/Polygraphy/polygraphy/tools/base/tool.py +++ b/tools/Polygraphy/polygraphy/tools/base/tool.py @@ -29,6 +29,7 @@ class Tool(object): """ Base class for CLI Tools. """ + def __init__(self, name=None): self.name = name @@ -39,7 +40,6 @@ class Tool(object): self.arg_groups = OrderedDict() self.subscribe_args(LoggerArgs()) - def subscribe_args(self, maker): """ Subscribe to an argument group. The argument group's arguments will be added @@ -52,12 +52,10 @@ class Tool(object): m_type = type(maker) self.arg_groups[m_type] = maker - def add_parser_args(self, parser): # Should be implemented by child classes to add custom arguments. pass - def setup_parser(self, subparsers=None): """ Set up a command-line argument parser. @@ -76,7 +74,9 @@ class Tool(object): allow_abbrev = all(not maker.disable_abbrev for maker in self.arg_groups.values()) if subparsers is not None: - parser = subparsers.add_parser(self.name, help=self.__doc__ , add_help=True, description=self.__doc__, allow_abbrev=allow_abbrev) + parser = subparsers.add_parser( + self.name, help=self.__doc__, add_help=True, description=self.__doc__, allow_abbrev=allow_abbrev + ) parser.set_defaults(subcommand=self) else: parser = argparse.ArgumentParser(add_help=True, description=self.__doc__, allow_abbrev=allow_abbrev) @@ -96,15 +96,14 @@ class Tool(object): try: self.add_parser_args(parser) except Exception as err: - G_LOGGER.warning("Could not register tool argument parser for: {:}\n" - "Note: Error was: {:}".format(self.name, err)) + G_LOGGER.warning( + "Could not register tool argument parser for: {:}\nNote: Error was: {:}".format(self.name, err) + ) return parser - def run(self, args): raise NotImplementedError("run() must be implemented by child classes") - def __call__(self, args): """ Calls this tool with the specified arguments. @@ -119,7 +118,6 @@ class Tool(object): G_LOGGER.module_info(polygraphy) return self.run(args) - def main(self): """ Set up and run this tool. This function serves as a replacement for a manually diff --git a/tools/Polygraphy/polygraphy/tools/convert/README.md b/tools/Polygraphy/polygraphy/tools/convert/README.md index 128604b2..ebb47a86 100644 --- a/tools/Polygraphy/polygraphy/tools/convert/README.md +++ b/tools/Polygraphy/polygraphy/tools/convert/README.md @@ -4,12 +4,17 @@ - [Introduction](#introduction) - [Usage](#usage) +- [Examples](#examples) ## Introduction The `convert` tool can be used to convert models to various formats. - +For example, this can be used to convert ONNX models to TensorRT. ## Usage See `polygraphy convert -h` for usage information. + +## Examples + +For examples, see [this directory](../../../examples/cli/convert) diff --git a/tools/Polygraphy/polygraphy/tools/convert/convert.py b/tools/Polygraphy/polygraphy/tools/convert/convert.py index d3c7d263..2e031527 100644 --- a/tools/Polygraphy/polygraphy/tools/convert/convert.py +++ b/tools/Polygraphy/polygraphy/tools/convert/convert.py @@ -17,21 +17,31 @@ import os from polygraphy import mod from polygraphy.logger import G_LOGGER -from polygraphy.tools.args import (DataLoaderArgs, ModelArgs, OnnxLoaderArgs, - OnnxSaveArgs, OnnxShapeInferenceArgs, - Tf2OnnxLoaderArgs, TfLoaderArgs, - TrtConfigArgs, TrtEngineLoaderArgs, - TrtEngineSaveArgs, TrtNetworkLoaderArgs, - TrtPluginLoaderArgs) +from polygraphy.tools.args import ( + DataLoaderArgs, + ModelArgs, + OnnxLoaderArgs, + OnnxSaveArgs, + OnnxShapeInferenceArgs, + Tf2OnnxLoaderArgs, + TfLoaderArgs, + TrtConfigArgs, + TrtEngineLoaderArgs, + TrtEngineSaveArgs, + TrtNetworkLoaderArgs, + TrtPluginLoaderArgs, +) from polygraphy.tools.base import Tool onnx_backend = mod.lazy_import("polygraphy.backend.onnx") trt_backend = mod.lazy_import("polygraphy.backend.trt") + class Convert(Tool): """ Convert models to other formats. """ + def __init__(self): super().__init__("convert") self.subscribe_args(ModelArgs(model_required=True)) @@ -40,40 +50,52 @@ class Convert(Tool): self.subscribe_args(OnnxShapeInferenceArgs()) self.subscribe_args(OnnxLoaderArgs()) self.subscribe_args(OnnxSaveArgs(output=False)) - self.subscribe_args(DataLoaderArgs()) # For int8 calibration + self.subscribe_args(DataLoaderArgs()) # For int8 calibration self.subscribe_args(TrtConfigArgs()) self.subscribe_args(TrtPluginLoaderArgs()) self.subscribe_args(TrtNetworkLoaderArgs()) self.subscribe_args(TrtEngineLoaderArgs()) self.subscribe_args(TrtEngineSaveArgs(output=False)) - def add_parser_args(self, parser): - parser.add_argument("-o", "--output", help="Path to save the converted model", - required=True) - parser.add_argument("--convert-to", - help="The format to attempt to convert the model to.", - choices=["onnx", "trt"]) + parser.add_argument("-o", "--output", help="Path to save the converted model", required=True) + parser.add_argument( + "--convert-to", + help="The format to attempt to convert the model to." + "'onnx-like-trt-network' is EXPERIMETNAL and converts a TensorRT network to a format usable for visualization. " + "See 'OnnxLikeFromNetwork' for details. ", + choices=["onnx", "trt", "onnx-like-trt-network"], + ) onnx_args = self.arg_groups[OnnxLoaderArgs].group - onnx_args.add_argument("--fp-to-fp16", help="Convert all floating point tensors in an ONNX model to 16-bit precision. " - "This is *not* needed in order to use TensorRT's fp16 precision, but may be useful for other backends. " - "Requires onnxmltools. ", - action="store_true", default=None) - + onnx_args.add_argument( + "--fp-to-fp16", + help="Convert all floating point tensors in an ONNX model to 16-bit precision. " + "This is *not* needed in order to use TensorRT's fp16 precision, but may be useful for other backends. " + "Requires onnxmltools. ", + action="store_true", + default=None, + ) def run(self, args): if not args.convert_to: _, ext = os.path.splitext(args.output) if ext not in ModelArgs.EXT_MODEL_TYPE_MAPPING: - G_LOGGER.exit("Could not automatically determine model type based on output path: {:}\n" - "Please specify the desired output format with --convert-to".format(args.output)) + G_LOGGER.critical( + "Could not automatically determine model type based on output path: {:}\n" + "Please specify the desired output format with --convert-to".format(args.output) + ) convert_type = ModelArgs.ModelType(ModelArgs.EXT_MODEL_TYPE_MAPPING[ext]) + elif args.convert_to == "onnx-like-trt-network": + convert_type = "onnx-like-trt-network" else: CONVERT_TO_MODEL_TYPE_MAPPING = {"onnx": "onnx", "trt": "engine"} convert_type = ModelArgs.ModelType(CONVERT_TO_MODEL_TYPE_MAPPING[args.convert_to]) - if convert_type.is_onnx(): + if convert_type == "onnx-like-trt-network": + onnx_like = trt_backend.onnx_like_from_network(self.arg_groups[TrtNetworkLoaderArgs].get_network_loader()) + onnx_backend.save_onnx(onnx_like, args.output) + elif convert_type.is_onnx(): model = self.arg_groups[OnnxLoaderArgs].load_onnx() if args.fp_to_fp16: model = onnx_backend.convert_to_fp16(model) @@ -82,4 +104,4 @@ class Convert(Tool): with self.arg_groups[TrtEngineLoaderArgs].build_engine() as engine: self.arg_groups[TrtEngineSaveArgs].save_engine(engine, args.output) else: - G_LOGGER.exit("Cannot convert to model type: {:}".format(convert_type)) + G_LOGGER.critical("Cannot convert to model type: {:}".format(convert_type)) diff --git a/tools/Polygraphy/polygraphy/tools/debug/README.md b/tools/Polygraphy/polygraphy/tools/debug/README.md index db8fc518..90c4a75d 100644 --- a/tools/Polygraphy/polygraphy/tools/debug/README.md +++ b/tools/Polygraphy/polygraphy/tools/debug/README.md @@ -12,6 +12,30 @@ The `debug` tool can help debug accuracy issues during inference. +All the `debug` tools work on the same general principles: + +1. Iteratively generate models and various other artifacts. + + For example, `debug precision` generates a new TensorRT engine each iteration with some number + of layers marked to run in a higher precision. `debug reduce` generates smaller and smaller subgraphs + from the provided ONNX model. + + In many cases, it is also useful to save other artifacts, such as tactic replay files, + from each iteration. + +2. Each iteration, check whether the model generated in that iteration is good or bad, and sort artifacts + into `good` and `bad` directories. + + In order to determine whether a model is good or bad, the subtool uses the `--check` command + provided by the user (that's you!). This can be any command that checks some aspect of the generated + model and determines whether it is a good or bad model. For example, to debug an accuracy issue, you + could use `polygraphy run --trt --load-outputs ` or some other accuracy validation + script. + + When the `--check` command exits with a failure (what qualifies as a "failure" can be controlled via various + command-line options like `--fail-regex` and `--fail-returncode`), the iteration is counted as a failure, and + any artifacts specified to `--artifacts` are moved into a `bad` directory. + ## Subtools @@ -22,6 +46,8 @@ The `debug` tool can help debug accuracy issues during inference. running `polygraphy run` repeatedly since some of the work, like model parsing, can be shared across iterations. + See the [example](../../../examples//cli/debug/01_debugging_flaky_trt_tactics/) for details. + - `precision` can be used to determine which layers of a TensorRT network need to be run in a higher precision in order to maintain the desired accuracy. @@ -31,6 +57,8 @@ The `debug` tool can help debug accuracy issues during inference. - `diff-tactics` can determine potentially bad tactics given a set of known-good tactic replay files and a set of bad ones. + See the [example](../../../examples//cli/debug/01_debugging_flaky_trt_tactics/) for details. + - [EXPERIMENTAL] `reduce` can reduce failing ONNX models to a minimal subgraph of failing nodes. This can make further debugging significantly easier. @@ -50,7 +78,8 @@ The `debug` tool can help debug accuracy issues during inference. that can determine the shapes to use for intermediate tensors. - [EXPERIMENTAL] `repeat` can run an arbitrary command repeatedly, sorting generated artifacts - into `good` and `bad` directories. + into `good` and `bad` directories. This is more general than the other `debug` subtools, and is + effectively equivalent to manually running a command repeatedly and moving files between runs. ## Usage diff --git a/tools/Polygraphy/polygraphy/tools/debug/debug.py b/tools/Polygraphy/polygraphy/tools/debug/debug.py index f7be322a..093891a7 100644 --- a/tools/Polygraphy/polygraphy/tools/debug/debug.py +++ b/tools/Polygraphy/polygraphy/tools/debug/debug.py @@ -14,18 +14,17 @@ # limitations under the License. # from polygraphy.tools.base import Tool -from polygraphy.tools.debug.subtool import (Build, DiffTactics, Precision, - Reduce, Repeat) +from polygraphy.tools.debug.subtool import Build, DiffTactics, Precision, Reduce, Repeat class Debug(Tool): """ [EXPERIMENTAL] Debug model accuracy issues. """ + def __init__(self): super().__init__("debug") - def add_parser_args(self, parser): subparsers = parser.add_subparsers(title="Debug Subtools", dest="subtool") subparsers.required = True diff --git a/tools/Polygraphy/polygraphy/tools/debug/subtool/artifact_sorter.py b/tools/Polygraphy/polygraphy/tools/debug/subtool/artifact_sorter.py index bce96b35..7abfda7c 100644 --- a/tools/Polygraphy/polygraphy/tools/debug/subtool/artifact_sorter.py +++ b/tools/Polygraphy/polygraphy/tools/debug/subtool/artifact_sorter.py @@ -29,69 +29,124 @@ from polygraphy.tools.args.base import BaseArgs class ArtifactSorterArgs(BaseArgs): def __init__(self, iter_art_default=None, prefer_artifacts=True, enable_iter_art=True): - assert iter_art_default or not enable_iter_art, "Must provide iter_art_default if intermediate artifact is enabled" + assert ( + iter_art_default or not enable_iter_art + ), "Must provide iter_art_default if intermediate artifact is enabled" super().__init__(disable_abbrev=True) self._iter_art_default = iter_art_default self._prefer_artifacts = prefer_artifacts self._enable_iter_art = enable_iter_art - def add_to_parser(self, parser): artifact_sorter_args = parser.add_argument_group("Artifact Sorting", "Options for sorting artifacts") - artifact_sorter_args.add_argument("--artifacts", help="Path(s) of artifacts to sort. " - "These will be moved into 'good' and 'bad' directories based on the exit status of " - "the `--check` command and suffixed with an iteration number, timestamp and return code. ", - nargs="+") - artifact_sorter_args.add_argument("--art-dir", "--artifacts-dir", metavar="DIR", dest="artifacts_dir", - help="The directory in which to move artifacts and sort them into 'good' and 'bad'. ") + artifact_sorter_args.add_argument( + "--artifacts", + help="Path(s) of artifacts to sort. " + "These will be moved into 'good' and 'bad' directories based on the exit status of " + "the `--check` command and suffixed with an iteration number, timestamp and return code. ", + nargs="+", + ) + artifact_sorter_args.add_argument( + "--art-dir", + "--artifacts-dir", + metavar="DIR", + dest="artifacts_dir", + help="The directory in which to move artifacts and sort them into 'good' and 'bad'. ", + ) - artifact_sorter_args.add_argument("--check", "--check-inference", dest="check", help="A command to check the model. " - "The command should return an exit status of 0 for the run to be considered 'good'. " - "Non-zero exit statuses are treated as 'bad' runs.", required=True, nargs=argparse.REMAINDER) + artifact_sorter_args.add_argument( + "--check", + "--check-inference", + dest="check", + help="A command to check the model. " + "The command should return an exit status of 0 for the run to be considered 'good'. " + "Non-zero exit statuses are treated as 'bad' runs.", + required=True, + nargs=argparse.REMAINDER, + ) - artifact_sorter_args.add_argument("--fail-code", "--fail-returncode", dest="fail_codes", - help="The return code(s) from the --check command to count as failures. " - "If this is provided, any other return code will be counted as a success. Useful for ignoring certain " - "types of failures. ", nargs="+", default=None, type=int) - artifact_sorter_args.add_argument("--fail-regex", dest="fail_regex", - help="Regular expression denoting an error in the check command's output. The command " - "is only considered a failure if a matching string is found in the command's output. " - "This can be useful to distinguish among multiple types of failures. " - "Can be specified multiple times to match different regular expressions, in which case any match counts as a failure. " - "When combined with --fail-code, only iterations which return one of the specified codes are " - "checked for regular expressions indicating failure.", - default=None, nargs="+") + fail_codes = artifact_sorter_args.add_mutually_exclusive_group() + fail_codes.add_argument( + "--fail-code", + "--fail-returncode", + dest="fail_codes", + help="The return code(s) from the --check command to count as failures. " + "If this is provided, any other return code will be counted as a success. ", + nargs="+", + default=None, + type=int, + ) + fail_codes.add_argument( + "--ignore-fail-code", + "--ignore-fail-returncode", + dest="ignore_fail_codes", + help="The return code(s) from the --check command to ignore as failures. ", + nargs="+", + default=None, + type=int, + ) - artifact_sorter_args.add_argument("--show-output", help="Show output from the --check command. Defaults to capturing output instead. ", - action="store_true") + artifact_sorter_args.add_argument( + "--fail-regex", + dest="fail_regex", + help="Regular expression denoting an error in the check command's output. The command " + "is only considered a failure if a matching string is found in the command's output. " + "This can be useful to distinguish among multiple types of failures. " + "Can be specified multiple times to match different regular expressions, in which case any match counts as a failure. " + "When combined with --fail-code, only iterations whose return code is considered a failure are " + "checked for regular expressions.", + default=None, + nargs="+", + ) + + artifact_sorter_args.add_argument( + "--show-output", + help="Show output from the --check command. Defaults to capturing output instead. ", + action="store_true", + ) if self._enable_iter_art: - artifact_sorter_args.add_argument("--iter-artifact", "--intermediate-artifact", dest="iter_artifact", - help="Path to store the intermediate artifact from each iteration. " - "Defaults to: {:}".format(self._iter_art_default), - default=self._iter_art_default) - artifact_sorter_args.add_argument("--no-remove-intermediate", help="Do not remove the intermediate artifact between iterations. " - "This allows you to exit the tool early and still have access to the intermediate artifact. ", - action="store_false", dest="remove_intermediate") - - artifact_sorter_args.add_argument("--iter-info", "--iteration-info", help="Path to write a JSON file containing information about " - "the current iteration. This will include an 'iteration' key specifying the current iteration. ", - dest="iteration_info", default=None) + artifact_sorter_args.add_argument( + "--iter-artifact", + "--intermediate-artifact", + dest="iter_artifact", + help="Path to store the intermediate artifact from each iteration. " + "Defaults to: {:}".format(self._iter_art_default), + default=self._iter_art_default, + ) + artifact_sorter_args.add_argument( + "--no-remove-intermediate", + help="Do not remove the intermediate artifact between iterations. " + "This allows you to exit the tool early and still have access to the intermediate artifact. ", + action="store_false", + dest="remove_intermediate", + ) + artifact_sorter_args.add_argument( + "--iter-info", + "--iteration-info", + help="Path to write a JSON file containing information about " + "the current iteration. This will include an 'iteration' key specifying the current iteration. ", + dest="iteration_info", + default=None, + ) def parse(self, args): self.iter_artifact = args_util.get(args, "iter_artifact") if self.iter_artifact and os.path.exists(self.iter_artifact): - G_LOGGER.exit("{:} already exists, refusing to overwrite.\n" - "Please specify a different path for the intermediate artifact with " - "--intermediate-artifact".format(self.iter_artifact)) + G_LOGGER.critical( + "{:} already exists, refusing to overwrite.\n" + "Please specify a different path for the intermediate artifact with " + "--intermediate-artifact".format(self.iter_artifact) + ) self.artifacts = util.default(args_util.get(args, "artifacts"), []) self.output = args_util.get(args, "artifacts_dir") self.show_output = args_util.get(args, "show_output") self.remove_intermediate = args_util.get(args, "remove_intermediate") self.fail_codes = args_util.get(args, "fail_codes") + self.ignore_fail_codes = args_util.get(args, "ignore_fail_codes") self.fail_regexes = None fail_regex = args_util.get(args, "fail_regex") @@ -100,14 +155,17 @@ class ArtifactSorterArgs(BaseArgs): for regex in fail_regex: self.fail_regexes.append(re.compile(regex)) - if self.artifacts and not self.output: - G_LOGGER.exit("An output directory must be specified if artifacts are enabled! " - "Note: Artifacts specified were: {:}".format(self.artifacts)) + G_LOGGER.critical( + "An output directory must be specified if artifacts are enabled! " + "Note: Artifacts specified were: {:}".format(self.artifacts) + ) if not self.artifacts and self._prefer_artifacts: - G_LOGGER.warning("`--artifacts` was not specified; No artifacts will be stored during this run! " - "Is this what you intended?") + G_LOGGER.warning( + "`--artifacts` was not specified; No artifacts will be stored during this run! " + "Is this what you intended?" + ) self.iteration_info = args_util.get(args, "iteration_info") @@ -116,7 +174,6 @@ class ArtifactSorterArgs(BaseArgs): self.start_date = time.strftime("%x").replace("/", "-") self.start_time = time.strftime("%X").replace(":", "-") - def sort_artifacts(self, iteration, suffix=None): """ Run the check command and move artifacts into the correct subdirectory. @@ -132,6 +189,7 @@ class ArtifactSorterArgs(BaseArgs): Returns: bool: True if the command succeeded, False otherwise. """ + def move_artifacts(subdir, returncode): """ Moves artifacts (args.artifacts) into the specified subdirectory or args.output and @@ -145,17 +203,23 @@ class ArtifactSorterArgs(BaseArgs): basename, ext = os.path.splitext(os.path.basename(art)) if suffix: basename += suffix - name = "{:}_{:}_{:}_N{:}_ret{:}{:}".format(basename, self.start_date, self.start_time, iteration, returncode, ext) + name = "{:}_{:}_{:}_N{:}_ret{:}{:}".format( + basename, self.start_date, self.start_time, iteration, returncode, ext + ) dest = os.path.join(self.output, subdir, name) if not os.path.exists(art): - G_LOGGER.error("Artifact: {:} does not exist, skipping.\n" - "Was the artifact supposed to be generated?".format(art)) + G_LOGGER.error( + "Artifact: {:} does not exist, skipping.\n" + "Was the artifact supposed to be generated?".format(art) + ) continue if os.path.exists(dest): - G_LOGGER.error("Destination path: {:} already exists.\n" - "Refusing to overwrite. This artifact will be skipped!".format(dest)) + G_LOGGER.error( + "Destination path: {:} already exists.\n" + "Refusing to overwrite. This artifact will be skipped!".format(dest) + ) continue G_LOGGER.info("Moving {:} to {:}".format(art, dest)) @@ -166,17 +230,19 @@ class ArtifactSorterArgs(BaseArgs): os.makedirs(dir_path, exist_ok=True) shutil.move(art, dest) - def try_remove(path): def func(): try: os.remove(path) except: G_LOGGER.verbose("Could not remove: {:}".format(path)) + return func - def is_success(status): + if self.ignore_fail_codes and status.returncode in self.ignore_fail_codes: + return True + has_fail_regex = None if self.fail_regexes is not None: output = status.stdout.decode() + status.stderr.decode() @@ -193,7 +259,6 @@ class ArtifactSorterArgs(BaseArgs): failed = status.returncode != 0 if has_fail_regex is None else has_fail_regex return not failed - with contextlib.ExitStack() as stack, G_LOGGER.indent(): if self.iter_artifact and self.remove_intermediate: stack.callback(try_remove(self.iter_artifact)) @@ -209,7 +274,10 @@ class ArtifactSorterArgs(BaseArgs): if self.show_output: stderr_log_level = G_LOGGER.WARNING if success else G_LOGGER.ERROR G_LOGGER.info("========== CAPTURED STDOUT ==========\n{:}".format(status.stdout.decode())) - G_LOGGER.log("========== CAPTURED STDERR ==========\n{:}".format(status.stderr.decode()), severity=stderr_log_level) + G_LOGGER.log( + "========== CAPTURED STDERR ==========\n{:}".format(status.stderr.decode()), + severity=stderr_log_level, + ) if success: move_artifacts("good", status.returncode) diff --git a/tools/Polygraphy/polygraphy/tools/debug/subtool/base.py b/tools/Polygraphy/polygraphy/tools/debug/subtool/base.py index e369e572..0d22a0fa 100644 --- a/tools/Polygraphy/polygraphy/tools/debug/subtool/base.py +++ b/tools/Polygraphy/polygraphy/tools/debug/subtool/base.py @@ -16,12 +16,19 @@ import contextlib import os -from polygraphy import mod, util +from polygraphy import mod, util, config from polygraphy.logger import G_LOGGER -from polygraphy.tools.args import (DataLoaderArgs, ModelArgs, OnnxLoaderArgs, - OnnxShapeInferenceArgs, TrtConfigArgs, - TrtEngineLoaderArgs, TrtEngineSaveArgs, - TrtNetworkLoaderArgs, TrtPluginLoaderArgs) +from polygraphy.tools.args import ( + DataLoaderArgs, + ModelArgs, + OnnxLoaderArgs, + OnnxShapeInferenceArgs, + TrtConfigArgs, + TrtEngineLoaderArgs, + TrtEngineSaveArgs, + TrtNetworkLoaderArgs, + TrtPluginLoaderArgs, +) from polygraphy.tools.base import Tool from polygraphy.tools.debug.subtool.artifact_sorter import ArtifactSorterArgs @@ -29,27 +36,25 @@ trt_backend = mod.lazy_import("polygraphy.backend.trt") class BaseCheckerSubtool(Tool): - def __init__(self, name, force_strict_types=None, prefer_artifacts=True): + def __init__(self, name, strict_types_default=None, prefer_artifacts=True): super().__init__(name) self.subscribe_args(ArtifactSorterArgs("polygraphy_debug.engine", prefer_artifacts=prefer_artifacts)) self.subscribe_args(ModelArgs(model_required=True, inputs=None)) self.subscribe_args(OnnxShapeInferenceArgs()) self.subscribe_args(OnnxLoaderArgs(output_prefix=None)) - self.subscribe_args(DataLoaderArgs()) # For int8 calibration - self.subscribe_args(TrtConfigArgs(force_strict_types=force_strict_types)) + self.subscribe_args(DataLoaderArgs()) # For int8 calibration + self.subscribe_args(TrtConfigArgs(strict_types_default=strict_types_default)) self.subscribe_args(TrtPluginLoaderArgs()) self.subscribe_args(TrtNetworkLoaderArgs()) self.subscribe_args(TrtEngineLoaderArgs()) self.subscribe_args(TrtEngineSaveArgs(output=False)) - def setup(self, args, network): """ Initialize a subtool. """ pass - def stop(self, iteration, success): """ Controls when to stop iteration. @@ -63,7 +68,6 @@ class BaseCheckerSubtool(Tool): """ raise NotImplementedError("Must be implemented by child classes!") - def process_network(self, network, prev_success): """ Process the TensorRT network prior to engine building. @@ -76,6 +80,11 @@ class BaseCheckerSubtool(Tool): """ pass + def remaining(self): + """ + Returns the estimated number of iterations remaining. + """ + pass def run(self, args): G_LOGGER.start("Starting iterations") @@ -94,17 +103,36 @@ class BaseCheckerSubtool(Tool): num_total = 0 success = True - MAX_COUNT = 100000 # We don't want to loop forever. This many iterations ought to be enough for anybody. + MAX_COUNT = 100000 # We don't want to loop forever. This many iterations ought to be enough for anybody. for iteration in range(MAX_COUNT): - G_LOGGER.start("RUNNING | Iteration {:}".format(iteration + 1)) + remaining = self.remaining() + G_LOGGER.start( + "RUNNING | Iteration {:}{:}".format( + iteration + 1, + " | Approximately {:} iteration(s) remaining".format(remaining) + if remaining is not None + else "", + ) + ) self.process_network(network, success) - # Don't need to keep the engine around in memory - just serialize to disk and free it. - with self.arg_groups[TrtEngineLoaderArgs].build_engine((builder, network)) as engine: - self.arg_groups[TrtEngineSaveArgs].save_engine(engine, self.arg_groups[ArtifactSorterArgs].iter_artifact) - - success = self.arg_groups[ArtifactSorterArgs].sort_artifacts(iteration + 1) + try: + engine = self.arg_groups[TrtEngineLoaderArgs].build_engine((builder, network)) + except Exception as err: + G_LOGGER.warning( + "Failed to create network or engine, continuing to the next iteration.\n" + "Note: Error was: {:}".format(err) + ) + G_LOGGER.internal_error("Failed to create network or engine. See warning above for details.") + success = False + else: + # Don't need to keep the engine around in memory - just serialize to disk and free it. + with engine: + self.arg_groups[TrtEngineSaveArgs].save_engine( + engine, self.arg_groups[ArtifactSorterArgs].iter_artifact + ) + success = self.arg_groups[ArtifactSorterArgs].sort_artifacts(iteration + 1) num_total += 1 if success: @@ -113,8 +141,13 @@ class BaseCheckerSubtool(Tool): if self.stop(iteration, success): break else: - G_LOGGER.warning("Maximum number of iterations reached: {:}.\n" - "Iteration has been halted to prevent an infinite loop!".format(MAX_COUNT)) + G_LOGGER.warning( + "Maximum number of iterations reached: {:}.\n" + "Iteration has been halted to prevent an infinite loop!".format(MAX_COUNT) + ) - G_LOGGER.finish("Finished {:} iteration(s) | Passed: {:}/{:} | Pass Rate: {:}%".format( - iteration + 1, num_passed, num_total, float(num_passed) * 100 / float(num_total))) + G_LOGGER.finish( + "Finished {:} iteration(s) | Passed: {:}/{:} | Pass Rate: {:}%".format( + iteration + 1, num_passed, num_total, float(num_passed) * 100 / float(num_total) + ) + ) diff --git a/tools/Polygraphy/polygraphy/tools/debug/subtool/build.py b/tools/Polygraphy/polygraphy/tools/debug/subtool/build.py index 8f4317ad..f4301035 100644 --- a/tools/Polygraphy/polygraphy/tools/debug/subtool/build.py +++ b/tools/Polygraphy/polygraphy/tools/debug/subtool/build.py @@ -24,15 +24,18 @@ class Build(BaseCheckerSubtool): into `good` and `bad` directories. Each iteration will generate an engine called 'polygraphy_debug.engine' in the current directory. """ + def __init__(self): super().__init__("build") - def add_parser_args(self, parser): - parser.add_argument("--until", required=True, help="Controls when to stop running. " - "Choices are: ['good', 'bad', int]. 'good' will keep running until the first 'good' run. " - "'bad' will run until the first 'bad' run. An integer can be specified to run a set number of iterations. ") - + parser.add_argument( + "--until", + required=True, + help="Controls when to stop running. " + "Choices are: ['good', 'bad', int]. 'good' will keep running until the first 'good' run. " + "'bad' will run until the first 'bad' run. An integer can be specified to run a set number of iterations. ", + ) def setup(self, args, network): try: @@ -40,8 +43,7 @@ class Build(BaseCheckerSubtool): except: self.until = args.until if self.until not in ["good", "bad"]: - G_LOGGER.exit("--until value must be an integer, 'good', or 'bad', but was: {:}".format(args.until)) - + G_LOGGER.critical("--until value must be an integer, 'good', or 'bad', but was: {:}".format(args.until)) def stop(self, index, success): if self.until == "good": diff --git a/tools/Polygraphy/polygraphy/tools/debug/subtool/diff_tactics.py b/tools/Polygraphy/polygraphy/tools/debug/subtool/diff_tactics.py index 7506d030..102658a1 100644 --- a/tools/Polygraphy/polygraphy/tools/debug/subtool/diff_tactics.py +++ b/tools/Polygraphy/polygraphy/tools/debug/subtool/diff_tactics.py @@ -30,23 +30,23 @@ class DiffTactics(Tool): Determine potentially bad tactics given sets of good and bad tactic replay files. """ + def __init__(self): super().__init__("diff-tactics") - def add_parser_args(self, parser): - parser.add_argument("--dir", help="A directory containing good and bad tactic replay files. " - "By default, this tool will search for files in directories called 'good' and 'bad'", - default="") + parser.add_argument( + "--dir", + help="A directory containing good and bad tactic replay files. " + "By default, this tool will search for files in directories called 'good' and 'bad'", + default="", + ) parser.add_argument("--good", help="A directory containing good tactic replay files. ", default=None) parser.add_argument("--bad", help="A directory containing bad tactic replay files. ", default=None) - - def run(self, args): if args.dir is None and (args.good is None or args.bad is None): - G_LOGGER.exit("Either `--dir`, or both `--good` and `--bad` must be specified.") - + G_LOGGER.critical("Either `--dir`, or both `--good` and `--bad` must be specified.") def load_tactics(dir): """ @@ -59,6 +59,7 @@ class DiffTactics(Tool): dict[str, Set[polygraphy.backend.trt.algorithm_selector.Algorithm]]: Maps layer names to the set of algorithms present in the tactic replays. """ + def try_load_replay(path): try: return algorithm_selector.TacticReplayData.load(path) @@ -78,7 +79,6 @@ class DiffTactics(Tool): tactics[name].add(algo) return tactics, replay_paths - good_dir = util.default(args.good, os.path.join(args.dir, "good")) good_tactics, good_paths = load_tactics(good_dir) G_LOGGER.info("Loaded {:} good tactic replays.".format(len(good_paths))) diff --git a/tools/Polygraphy/polygraphy/tools/debug/subtool/precision.py b/tools/Polygraphy/polygraphy/tools/debug/subtool/precision.py index f6a7a87e..94c7b400 100644 --- a/tools/Polygraphy/polygraphy/tools/debug/subtool/precision.py +++ b/tools/Polygraphy/polygraphy/tools/debug/subtool/precision.py @@ -28,11 +28,13 @@ trt_util = mod.lazy_import("polygraphy.backend.trt.util") class BaseMarker(object): def __init__(self, max_layers, direction, num_layers): self.max_layers = max_layers - self.num_layers = num_layers self.direction = direction - + self.num_layers = num_layers + self.good = max_layers + 1 # Pretend marking all the layers gives us good accuracy. + self.iteration = 0 def select_layers(self): + self.iteration += 1 if self.direction == "forward": G_LOGGER.info("Selecting first {:} layer(s) to run in higher precision".format(self.num_layers)) return range(0, self.num_layers) @@ -40,20 +42,19 @@ class BaseMarker(object): G_LOGGER.info("Selecting last {:} layer(s) to run in higher precision".format(self.num_layers)) return range(self.max_layers - self.num_layers, self.max_layers) - def success_message(self): which_layers = "first" if self.direction == "forward" else "last" - G_LOGGER.finish("To achieve acceptable accuracy, try running the {:} {:} " - "layer(s) in higher precision".format(which_layers, self.num_layers)) + G_LOGGER.finish( + "To achieve acceptable accuracy, try running the {:} {:} " + "layer(s) in higher precision".format(which_layers, self.good) + ) class BisectMarker(BaseMarker): def __init__(self, max_layers, direction) -> None: super().__init__(max_layers, direction, max_layers) - self.good = self.max_layers + 1 # Pretend marking all the layers gives us good accuracy. self.bad = 0 - def select_layers(self, prev_success): if prev_success: self.good = self.num_layers @@ -66,7 +67,6 @@ class BisectMarker(BaseMarker): self.num_layers = round_func((self.good + self.bad) / 2.0) return super().select_layers() - def stop(self, index, success): # If good and bad are within 1 layer of each other, # then we already have the information we need. @@ -82,17 +82,20 @@ class BisectMarker(BaseMarker): return True return False + def remaining(self): + return int(math.log2(self.max_layers) - self.iteration) + class LinearMarker(BaseMarker): def __init__(self, max_layers, direction) -> None: super().__init__(max_layers, direction, 0) - def select_layers(self, prev_success): + if prev_success: + self.good = self.num_layers self.num_layers += 1 return super().select_layers() - def stop(self, index, success): if success: self.success_message() @@ -103,6 +106,9 @@ class LinearMarker(BaseMarker): return True return False + def remaining(self): + return self.max_layers - self.iteration + class Precision(BaseCheckerSubtool): """ @@ -110,38 +116,65 @@ class Precision(BaseCheckerSubtool): compromise between performance and quality. Each iteration will generate an engine called 'polygraphy_debug.engine' in the current directory. """ - def __init__(self): - super().__init__("precision", force_strict_types=True, prefer_artifacts=False) + def __init__(self): + super().__init__("precision", strict_types_default=True, prefer_artifacts=False) def add_parser_args(self, parser): - parser.add_argument("--mode", help="How layers are selected to run in higher precision. " - "'bisect' will use binary search, and 'linear' will iteratively mark one extra layer at a time", - choices=["bisect", "linear"], default="bisect") - parser.add_argument("--dir", "--direction", help="Order in which layers are marked to run in higher precision. " - "'forward' will start marking layers from network inputs, and 'reverse' will start " - "from the network outputs", choices=["forward", "reverse"], default="reverse", dest="direction") - parser.add_argument("-p", "--precision", help="Precision to use when marking layers to run in higher precision", - choices=["fp32", "fp16"], default="fp32") - + parser.add_argument( + "--mode", + help="How layers are selected to run in higher precision. " + "'bisect' will use binary search, and 'linear' will iteratively mark one extra layer at a time", + choices=["bisect", "linear"], + default="bisect", + ) + parser.add_argument( + "--dir", + "--direction", + help="Order in which layers are marked to run in higher precision. " + "'forward' will start marking layers from network inputs, and 'reverse' will start " + "from the network outputs", + choices=["forward", "reverse"], + default="forward", + dest="direction", + ) + parser.add_argument( + "-p", + "--precision", + help="Precision to use when marking layers to run in higher precision", + choices=["fp32", "fp16"], + default="fp32", + ) def setup(self, args, network): self.precision = {"fp32": trt.float32, "fp16": trt.float16}[args.precision] if self.precision == trt.float16 and not self.arg_groups[TrtConfigArgs].fp16: - G_LOGGER.exit("Cannot mark layers to run in fp16 if it is not enabled in the builder configuration.\n" - "Please also specify `--fp16` as a command-line option") + G_LOGGER.critical( + "Cannot mark layers to run in fp16 if it is not enabled in the builder configuration.\n" + "Please also specify `--fp16` as a command-line option" + ) if self.precision == trt.float16 and not self.arg_groups[TrtConfigArgs].int8: - G_LOGGER.warning("Using fp16 as the higher precision, but fp16 is also the lowest precision available. " - "Did you mean to set --int8 as well?") + G_LOGGER.warning( + "Using fp16 as the higher precision, but fp16 is also the lowest precision available. " + "Did you mean to set --int8 as well?" + ) - if not any([self.arg_groups[TrtConfigArgs].tf32, self.arg_groups[TrtConfigArgs].fp16, self.arg_groups[TrtConfigArgs].int8]): - G_LOGGER.exit("Please enable at least one precision besides fp32 (e.g. --int8, --fp16, --tf32)") + if not any( + [ + self.arg_groups[TrtConfigArgs].tf32, + self.arg_groups[TrtConfigArgs].fp16, + self.arg_groups[TrtConfigArgs].int8, + ] + ): + G_LOGGER.critical("Please enable at least one precision besides fp32 (e.g. --int8, --fp16, --tf32)") if self.arg_groups[ModelArgs].model_type == "engine": - G_LOGGER.exit("The precision tool cannot work with engines, as they cannot be modified. " - "Please provide a different format, such as an ONNX or TensorFlow model.") + G_LOGGER.critical( + "The precision tool cannot work with engines, as they cannot be modified. " + "Please provide a different format, such as an ONNX or TensorFlow model." + ) G_LOGGER.start("Using {:} as higher precision".format(self.precision)) @@ -150,23 +183,39 @@ class Precision(BaseCheckerSubtool): elif args.mode == "bisect": self.layer_marker = BisectMarker(len(network), args.direction) - def mark_layers(self, network, indices): + EXCLUDE_LAYER_NAMES = ["CONSTANT"] + EXCLUDE_LAYERS = [getattr(trt.LayerType, attr) for attr in EXCLUDE_LAYER_NAMES if hasattr(trt.LayerType, attr)] + # First, reset, since changes from the previous call will persist. for layer in network: layer.reset_precision() + marked_indices = set() for index in indices: layer = network.get_layer(index) - G_LOGGER.verbose("Running layer in higher precision: {:}".format(trt_util.str_from_layer(layer, index))) - layer.precision = self.precision - G_LOGGER.verbose("Marking layer(s): {:} to run in {:} precision".format(indices, self.precision)) + def should_exclude(): + has_non_execution_output = any( + not layer.get_output(i).is_execution_tensor for i in range(layer.num_outputs) + ) + return layer.type in EXCLUDE_LAYERS or has_non_execution_output + + if not should_exclude(): + G_LOGGER.extra_verbose( + "Running layer in higher precision: {:}".format(trt_util.str_from_layer(layer, index)) + ) + layer.precision = self.precision + marked_indices.add(index) + + G_LOGGER.verbose("Marking layer(s): {:} to run in {:} precision".format(marked_indices, self.precision)) def process_network(self, network, prev_success): indices = list(self.layer_marker.select_layers(prev_success)) self.mark_layers(network, indices) - def stop(self, index, success): return self.layer_marker.stop(index, success) + + def remaining(self): + return self.layer_marker.remaining() diff --git a/tools/Polygraphy/polygraphy/tools/debug/subtool/reduce.py b/tools/Polygraphy/polygraphy/tools/debug/subtool/reduce.py index a0d1365d..ec5c6e6a 100644 --- a/tools/Polygraphy/polygraphy/tools/debug/subtool/reduce.py +++ b/tools/Polygraphy/polygraphy/tools/debug/subtool/reduce.py @@ -19,8 +19,7 @@ import math from polygraphy import mod from polygraphy.logger.logger import G_LOGGER from polygraphy.tools import util as tools_util -from polygraphy.tools.args import (DataLoaderArgs, ModelArgs, OnnxLoaderArgs, - OnnxSaveArgs, OnnxShapeInferenceArgs) +from polygraphy.tools.args import DataLoaderArgs, ModelArgs, OnnxLoaderArgs, OnnxSaveArgs, OnnxShapeInferenceArgs from polygraphy.tools.base import Tool from polygraphy.tools.debug.subtool.artifact_sorter import ArtifactSorterArgs @@ -33,6 +32,7 @@ class MarkerBase(object): """ Controls how layers are marked for reduction. """ + def __init__(self, num_nodes, node_index): self.num_nodes = num_nodes self.iteration = 0 @@ -48,7 +48,6 @@ class MarkerBase(object): self._good_node_indices = {} self.best_good_node_index = None - def step(self, success, num_nodes): self.iteration += 1 if not success and num_nodes <= self._least_bad_nodes: @@ -58,11 +57,9 @@ class MarkerBase(object): if success: self._good_node_indices[num_nodes] = self.node_index - def _clamp(self, x, min_val, max_val): return max(min(x, max_val), min_val) - def finish(self): # Find the index of the node that has the highest number of nodes less than _least_bad_nodes, but still is successful. # Failing that, use the smallest possible subgraph (which will always be > _least_bad_nodes) @@ -83,20 +80,17 @@ class LinearMarker(MarkerBase): super().__init__(num_nodes, node_index=num_nodes - 1 if not invert else 0) self.invert = invert - def step(self, success, num_nodes): super().step(success, num_nodes) self.node_index += -1 if not self.invert else 1 return self.node_index - def stop(self): return (self.node_index < 0) or (self.node_index >= self.num_nodes) - def remaining(self): - return self.num_nodes - self.iteration - 1 + return self.num_nodes - self.iteration class BisectMarker(MarkerBase): @@ -110,7 +104,6 @@ class BisectMarker(MarkerBase): if invert: self.good, self.bad = self.bad, self.good - # Take a step in bisection. # This will return the index of the next node to try depending on the status of the previous run. def step(self, success, num_nodes): @@ -126,13 +119,11 @@ class BisectMarker(MarkerBase): self.node_index = round_func((self.good + self.bad) / 2.0) return self.node_index - def stop(self): return abs(self.good - self.bad) <= 1 - def remaining(self): - return int(math.log2(self.num_nodes) - self.iteration - 1) + return int(math.log2(self.num_nodes) - self.iteration) class Reduce(Tool): @@ -140,6 +131,7 @@ class Reduce(Tool): [EXPERIMENTAL] Reduce a failing ONNX model to the minimum set of nodes that cause the failure. Each iteration will generate an ONNX model called 'polygraphy_debug.onnx' in the current directory. """ + def __init__(self): super().__init__("reduce") self.subscribe_args(ArtifactSorterArgs("polygraphy_debug.onnx", prefer_artifacts=False)) @@ -147,32 +139,46 @@ class Reduce(Tool): self.subscribe_args(OnnxSaveArgs()) self.subscribe_args(OnnxShapeInferenceArgs(default=True, enable_force_fallback=True)) self.subscribe_args(OnnxLoaderArgs(output_prefix=None)) - self.subscribe_args(DataLoaderArgs()) # For fallback shape inference - + self.subscribe_args(DataLoaderArgs()) # For fallback shape inference def add_parser_args(self, parser): - parser.add_argument("--min-good", "--minimal-good", dest="min_good", - help="Path at which to save an ONNX model close in size to the reduced model " - "that does not have the failure. This is not guaranteed to be generated.") + parser.add_argument( + "--min-good", + "--minimal-good", + dest="min_good", + help="Path at which to save an ONNX model close in size to the reduced model " + "that does not have the failure. This is not guaranteed to be generated.", + ) disable_passes = parser.add_mutually_exclusive_group() - disable_passes.add_argument("--no-reduce-inputs", help="Do not attempt to change the graph inputs to reduce the model further. " - "'reduce' will then only attempt to find the earliest failing outputs. ", - action="store_false", dest="reduce_inputs") - disable_passes.add_argument("--no-reduce-outputs", help="Do not attempt to change the graph outputs to reduce the model further. " - "'reduce' will then only attempt to find the latest failing inputs. ", - action="store_false", dest="reduce_outputs") - - parser.add_argument("--mode", help="Strategy to use to iteratively remove nodes from the model. " - "'bisect' will use binary search, and 'linear' will delete one node at a time. " - "'linear' mode may be significantly slower, but can offer better results in models with branches. " - "One strategy is to use 'bisect' first, and then further reduce the result with 'linear'. ", - choices=["bisect", "linear"], default="bisect") + disable_passes.add_argument( + "--no-reduce-inputs", + help="Do not attempt to change the graph inputs to reduce the model further. " + "'reduce' will then only attempt to find the earliest failing outputs. ", + action="store_false", + dest="reduce_inputs", + ) + disable_passes.add_argument( + "--no-reduce-outputs", + help="Do not attempt to change the graph outputs to reduce the model further. " + "'reduce' will then only attempt to find the latest failing inputs. ", + action="store_false", + dest="reduce_outputs", + ) + parser.add_argument( + "--mode", + help="Strategy to use to iteratively remove nodes from the model. " + "'bisect' will use binary search, and 'linear' will delete one node at a time. " + "'linear' mode may be significantly slower, but can offer better results in models with branches. " + "One strategy is to use 'bisect' first, and then further reduce the result with 'linear'. ", + choices=["bisect", "linear"], + default="bisect", + ) def run(self, args): if not self.arg_groups[OnnxSaveArgs].path and not args.min_good: - G_LOGGER.exit("Either --output or --min-good must be provided!") + G_LOGGER.critical("Either --output or --min-good must be provided!") model = self.arg_groups[OnnxLoaderArgs].load_onnx() num_orig_nodes = len(model.graph.node) @@ -194,21 +200,22 @@ class Reduce(Tool): def layerwise(model, include_data=False): nonlocal _layerwise_outputs, _layerwise_meta if _layerwise_outputs is None or _layerwise_meta is None: - G_LOGGER.info("Running inference with ONNX-Runtime to determine metadata for intermediate tensors.\n" - "This will cause intermediate models to have static shapes.") + G_LOGGER.info( + "Running inference with ONNX-Runtime to determine metadata for intermediate tensors.\n" + "This will cause intermediate models to have static shapes." + ) _layerwise_outputs, _layerwise_meta = self.arg_groups[OnnxShapeInferenceArgs].fallback_inference(model) return _layerwise_outputs if include_data else _layerwise_meta - if self.arg_groups[OnnxShapeInferenceArgs].force_fallback: G_LOGGER.info("Freezing shapes in the model according to values determined by fallback shape inference") tools_util.set_shapes_from_layerwise_meta(GRAPH, layerwise(model)) - def fix_graph(graph, model): """ Fix the graph so it is valid ONNX. """ + def fix_tensor_metadata(tensors, fix_shape=True): for tensor in tensors: if not tensor.shape and fix_shape: @@ -236,29 +243,27 @@ class Reduce(Tool): return graph - def mark_io(graph, attr, tensors, filter_const=True): if filter_const: tensors = [t for t in tensors if not isinstance(t, gs.Constant)] if not tensors: - G_LOGGER.warning("No non-constant tensors are available to mark. " - "Try folding constants in the model with `polygraphy surgeon sanitize --fold-constants`") + G_LOGGER.warning( + "No non-constant tensors are available to mark. " + "Try folding constants in the model with `polygraphy surgeon sanitize --fold-constants`" + ) setattr(graph, attr, tensors) G_LOGGER.info("Marking model {attr}: {:}".format(getattr(graph, attr), attr=attr)) return graph - def names_from_tensors(tensors): return [t.name for t in tensors] - def lookup_tensors(graph, names): tensor_map = graph.tensors() return [tensor_map[name] for name in names] - # Bisect using the given marker, and modifying the given graph attribute. # attr should be one of ["inputs", "outputs"]. # filter_const indicates whether to filter out constant tensors before updating graph I/O. @@ -267,17 +272,25 @@ class Reduce(Tool): iter_graph = graph while not marker.stop(): - G_LOGGER.start("RUNNING | Iteration {:} | Approximately {:} iteration(s) remaining".format(marker.iteration + 1, marker.remaining())) - iter_graph = graph.copy() # This is a very light-weight copy of the entire graph. + G_LOGGER.start( + "RUNNING | Iteration {:} | Approximately {:} iteration(s) remaining".format( + marker.iteration + 1, marker.remaining() + ) + ) + iter_graph = graph.copy() # This is a very light-weight copy of the entire graph. with G_LOGGER.indent(): io_list = list(getattr(iter_graph.nodes[marker.node_index], attr)) mark_io(iter_graph, attr, io_list, filter_const) iter_graph.cleanup() - self.arg_groups[OnnxSaveArgs].save_onnx(gs.export_onnx(fix_graph(iter_graph, model)), self.arg_groups[ArtifactSorterArgs].iter_artifact) + self.arg_groups[OnnxSaveArgs].save_onnx( + gs.export_onnx(fix_graph(iter_graph, model)), self.arg_groups[ArtifactSorterArgs].iter_artifact + ) num_nodes = len(iter_graph.nodes) - success = self.arg_groups[ArtifactSorterArgs].sort_artifacts(marker.iteration + 1, suffix="_reduce_{:}_{:}_nodes".format(attr, num_nodes)) + success = self.arg_groups[ArtifactSorterArgs].sort_artifacts( + marker.iteration + 1, suffix="_reduce_{:}_{:}_nodes".format(attr, num_nodes) + ) marker.step(success, num_nodes) marker.finish() @@ -291,7 +304,6 @@ class Reduce(Tool): return get_io(marker.best_bad_node_index), get_io(marker.best_good_node_index) - # We reduce the model in 2 phases: # 1. Find the earliest output nodes that cause a failure. # 2. Find the latest input nodes cause a failure. @@ -311,7 +323,9 @@ class Reduce(Tool): bad_outputs, good_outputs = bisect_io(bad_graph, model, out_marker, attr="outputs", filter_const=False) bad_graph = mark_io(bad_graph, "outputs", lookup_tensors(bad_graph, bad_outputs)).cleanup() if good_graph is not None: - good_graph = mark_io(good_graph, "outputs", lookup_tensors(good_graph, good_outputs)) # Defer cleanup where possible. + good_graph = mark_io( + good_graph, "outputs", lookup_tensors(good_graph, good_outputs) + ) # Defer cleanup where possible. # Export the model with the reduced outputs so that reducing inputs is faster. model = gs.export_onnx(fix_graph(bad_graph, model)) @@ -322,7 +336,9 @@ class Reduce(Tool): bad_inputs, good_inputs = bisect_io(bad_graph, model, in_marker, attr="inputs") bad_graph = mark_io(bad_graph, "inputs", lookup_tensors(bad_graph, bad_inputs)).cleanup() if good_graph is not None: - good_graph = mark_io(good_graph, "inputs", lookup_tensors(good_graph, good_inputs)) # Defer cleanup where possible. + good_graph = mark_io( + good_graph, "inputs", lookup_tensors(good_graph, good_inputs) + ) # Defer cleanup where possible. # == Write Bad Model == @@ -331,9 +347,15 @@ class Reduce(Tool): if self.arg_groups[OnnxSaveArgs].path: num_reduced_nodes = len(reduced_model.graph.node) - if float(num_reduced_nodes) / float(num_orig_nodes) >= 0.25 and num_reduced_nodes > 1 and args.mode == "bisect": - G_LOGGER.warning("It looks like this model could potentially be reduced further.\n" - "You may want to reduce {:} again using --mode=linear. ".format(self.arg_groups[OnnxSaveArgs].path)) + if ( + float(num_reduced_nodes) / float(num_orig_nodes) >= 0.25 + and num_reduced_nodes > 1 + and args.mode == "bisect" + ): + G_LOGGER.warning( + "It looks like this model could potentially be reduced further.\n" + "You may want to reduce {:} again using --mode=linear. ".format(self.arg_groups[OnnxSaveArgs].path) + ) G_LOGGER.info("Minimum Bad Model:\n{:}\n\n".format(onnx_util.str_from_onnx(reduced_model, mode="none"))) self.arg_groups[OnnxSaveArgs].save_onnx(reduced_model) @@ -343,7 +365,11 @@ class Reduce(Tool): if good_graph is not None: min_good_model = gs.export_onnx(fix_graph(good_graph.cleanup(), model)) if min_good_model == reduced_model: - G_LOGGER.warning("Could not find a minimal model close in size to the reduced model that does not cause a failure.") + G_LOGGER.warning( + "Could not find a minimal model close in size to the reduced model that does not cause a failure." + ) else: - G_LOGGER.info("Minimum Good Model:\n{:}\n\n".format(onnx_util.str_from_onnx(min_good_model, mode="none"))) + G_LOGGER.info( + "Minimum Good Model:\n{:}\n\n".format(onnx_util.str_from_onnx(min_good_model, mode="none")) + ) self.arg_groups[OnnxSaveArgs].save_onnx(min_good_model, args.min_good) diff --git a/tools/Polygraphy/polygraphy/tools/debug/subtool/repeat.py b/tools/Polygraphy/polygraphy/tools/debug/subtool/repeat.py index 8f637424..a58e7857 100644 --- a/tools/Polygraphy/polygraphy/tools/debug/subtool/repeat.py +++ b/tools/Polygraphy/polygraphy/tools/debug/subtool/repeat.py @@ -24,16 +24,19 @@ class Repeat(Tool): [EXPERIMENTAL] Run an arbitrary command repeatedly, sorting generated artifacts into `good` and `bad` directories. """ + def __init__(self): super().__init__("repeat") self.subscribe_args(ArtifactSorterArgs(enable_iter_art=False)) - def add_parser_args(self, parser): - parser.add_argument("--until", required=True, help="Controls when to stop running. " - "Choices are: ['good', 'bad', int]. 'good' will keep running until the first 'good' run. " - "'bad' will run until the first 'bad' run. An integer can be specified to run a set number of iterations. ") - + parser.add_argument( + "--until", + required=True, + help="Controls when to stop running. " + "Choices are: ['good', 'bad', int]. 'good' will keep running until the first 'good' run. " + "'bad' will run until the first 'bad' run. An integer can be specified to run a set number of iterations. ", + ) def run(self, args): try: @@ -41,8 +44,7 @@ class Repeat(Tool): except: until = args.until if until not in ["good", "bad"]: - G_LOGGER.exit("--until value must be an integer, 'good', or 'bad', but was: {:}".format(args.until)) - + G_LOGGER.critical("--until value must be an integer, 'good', or 'bad', but was: {:}".format(args.until)) def stop(index, success): if until == "good": @@ -52,14 +54,13 @@ class Repeat(Tool): return index >= until - G_LOGGER.start("Starting iterations") num_passed = 0 num_total = 0 success = True - MAX_COUNT = 100000 # We don't want to loop forever. This many iterations ought to be enough for anybody. + MAX_COUNT = 100000 # We don't want to loop forever. This many iterations ought to be enough for anybody. for iteration in range(MAX_COUNT): G_LOGGER.start("RUNNING | Iteration {:}".format(iteration + 1)) @@ -72,8 +73,13 @@ class Repeat(Tool): if stop(iteration, success): break else: - G_LOGGER.warning("Maximum number of iterations reached: {:}.\n" - "Iteration has been halted to prevent an infinite loop!".format(MAX_COUNT)) + G_LOGGER.warning( + "Maximum number of iterations reached: {:}.\n" + "Iteration has been halted to prevent an infinite loop!".format(MAX_COUNT) + ) - G_LOGGER.finish("Finished {:} iteration(s) | Passed: {:}/{:} | Pass Rate: {:}%".format( - iteration + 1, num_passed, num_total, float(num_passed) * 100 / float(num_total))) + G_LOGGER.finish( + "Finished {:} iteration(s) | Passed: {:}/{:} | Pass Rate: {:}%".format( + iteration + 1, num_passed, num_total, float(num_passed) * 100 / float(num_total) + ) + ) diff --git a/tools/Polygraphy/polygraphy/tools/inspect/inspect.py b/tools/Polygraphy/polygraphy/tools/inspect/inspect.py index ce688618..b3497868 100644 --- a/tools/Polygraphy/polygraphy/tools/inspect/inspect.py +++ b/tools/Polygraphy/polygraphy/tools/inspect/inspect.py @@ -21,10 +21,10 @@ class Inspect(Tool): """ View information about various types of files. """ + def __init__(self): super().__init__("inspect") - def add_parser_args(self, parser): subparsers = parser.add_subparsers(title="Inspection Subtools", dest="subtool") subparsers.required = True diff --git a/tools/Polygraphy/polygraphy/tools/inspect/subtool/data.py b/tools/Polygraphy/polygraphy/tools/inspect/subtool/data.py index 276a19b2..9eba0cb9 100644 --- a/tools/Polygraphy/polygraphy/tools/inspect/subtool/data.py +++ b/tools/Polygraphy/polygraphy/tools/inspect/subtool/data.py @@ -27,18 +27,23 @@ class Data(Tool): Display information about inference inputs and outputs saved from Polygraphy's Comparator.run() (for example, outputs saved by `--save-outputs` or inputs saved by `--save-inputs` from `polygraphy run`). """ + def __init__(self): super().__init__("data") - def add_parser_args(self, parser): parser.add_argument("path", help="Path to a file containing input or output data from Polygraphy") - parser.add_argument("-a", "--all", help="Show information on all iterations present in the data instead of just the first", - action="store_true") - parser.add_argument("-s", "--show-values", help="Show values of the tensors instead of just metadata", action="store_true") + parser.add_argument( + "-a", + "--all", + help="Show information on all iterations present in the data instead of just the first", + action="store_true", + ) + parser.add_argument( + "-s", "--show-values", help="Show values of the tensors instead of just metadata", action="store_true" + ) parser.add_argument("--histogram", help="Show a histogram of the value distribution", action="store_true") - def run(self, args): # Note: It's important we have encode/decode JSON methods registered # for the types we care about, e.g. RunResults. Importing the class should generally guarantee this. @@ -50,53 +55,46 @@ class Data(Tool): meta.add(name, dtype=arr.dtype, shape=arr.shape) return meta - def str_from_iters(iters): out_str = "" for index, iter_result in enumerate(iters): - if args.show_values: - for name, arr in iter_result.items(): - out_str += "{:} [dtype={:}, shape={:}]\n{:}\n".format(name, arr.dtype, arr.shape, util.indent_block(str(arr))) - else: - iter_meta = meta_from_iter_result(iter_result) - if len(iters) > 1 and args.all: - out_str += util.indent_block("Iteration: {:} | ".format(index)) - out_str += "{:}\n".format(iter_meta) + iter_meta = meta_from_iter_result(iter_result) + if len(iters) > 1 and args.all: + out_str += util.indent_block("Iteration: {:} | ".format(index)) - stat_str = "\n-- Statistics --" for name, arr in iter_result.items(): - stat_str += "\n{:} | Stats\n".format(name) - stat_str += util.indent_block(comp_util.str_output_stats(arr)) + "\n" + out_str += "\n{:} {:} | Stats\n".format(name, iter_meta[name]) + out_str += util.indent_block(comp_util.str_output_stats(arr)) + "\n" if args.histogram: - stat_str += util.indent_block(comp_util.str_histogram(arr)) + "\n" - - out_str += stat_str + out_str += util.indent_block(comp_util.str_histogram(arr)) + "\n" + if args.show_values: + out_str += "{:}\n".format(util.indent_block(str(arr))) if not args.all: break return out_str - def display_results(results): results_str = "" results_str += "==== Run Results ({:} runners) ====\n\n".format(len(results)) + max_runner_width = max(len(runner_name) for runner_name in results.keys()) for runner_name, iters in results.items(): - results_str += "---- {:35} ({:} iterations) ----\n".format(runner_name, len(iters)) + results_str += "---- {:<{max_runner_width}} ({:} iterations) ----\n".format( + runner_name, len(iters), max_runner_width=max_runner_width + ) results_str += str_from_iters(iters) + "\n" results_str = util.indent_block(results_str, level=0).strip() G_LOGGER.info(results_str) - def display_inputs(input_data): inputs_str = "" - inputs_str += "==== Data ({:} iterations) ====\n\n".format(len(input_data)) + inputs_str += "==== Data ({:} iterations) ====\n".format(len(input_data)) inputs_str += str_from_iters(input_data) + "\n" inputs_str = util.indent_block(inputs_str, level=0).strip() G_LOGGER.info(inputs_str) - if isinstance(data, RunResults): display_results(data) else: diff --git a/tools/Polygraphy/polygraphy/tools/inspect/subtool/model.py b/tools/Polygraphy/polygraphy/tools/inspect/subtool/model.py index c2e44a64..f3a81178 100644 --- a/tools/Polygraphy/polygraphy/tools/inspect/subtool/model.py +++ b/tools/Polygraphy/polygraphy/tools/inspect/subtool/model.py @@ -17,10 +17,15 @@ import contextlib from polygraphy import mod, util from polygraphy.logger import G_LOGGER -from polygraphy.tools.args import (ModelArgs, OnnxLoaderArgs, - OnnxShapeInferenceArgs, TfLoaderArgs, - TrtEngineLoaderArgs, TrtNetworkLoaderArgs, - TrtPluginLoaderArgs) +from polygraphy.tools.args import ( + ModelArgs, + OnnxLoaderArgs, + OnnxShapeInferenceArgs, + TfLoaderArgs, + TrtEngineLoaderArgs, + TrtNetworkLoaderArgs, + TrtPluginLoaderArgs, +) from polygraphy.tools.base import Tool trt_util = mod.lazy_import("polygraphy.backend.trt.util") @@ -32,6 +37,7 @@ class Model(Tool): """ Display information about a model, including inputs and outputs, as well as layers and their attributes. """ + def __init__(self): super().__init__("model") self.subscribe_args(ModelArgs(model_required=True, inputs=None)) @@ -42,19 +48,27 @@ class Model(Tool): self.subscribe_args(TrtNetworkLoaderArgs(outputs=False)) self.subscribe_args(TrtEngineLoaderArgs()) - def add_parser_args(self, parser): - parser.add_argument("--convert-to", "--display-as", - help="Try to convert the model to the specified format before displaying", - choices=["trt"], dest="display_as") - parser.add_argument("--mode", "--layer-info", help="Display layers: {{" - "'none': Display no layer information, " - "'basic': Display layer inputs and outputs, " - "'attrs': Display layer inputs, outputs and attributes, " - "'full': Display layer inputs, outputs, attributes, and weights" - "}}", - choices=["none", "basic", "attrs", "full"], dest="mode", default="none") - + parser.add_argument( + "--convert-to", + "--display-as", + help="Try to convert the model to the specified format before displaying", + choices=["trt"], + dest="display_as", + ) + parser.add_argument( + "--mode", + "--layer-info", + help="Display layers: {{" + "'none': Display no layer information, " + "'basic': Display layer inputs and outputs, " + "'attrs': Display layer inputs, outputs and attributes, " + "'full': Display layer inputs, outputs, attributes, and weights" + "}}", + choices=["none", "basic", "attrs", "full"], + dest="mode", + default="none", + ) def run(self, args): func = None @@ -69,11 +83,10 @@ class Model(Tool): func = self.inspect_trt if func is None: - G_LOGGER.exit("Could not determine how to display this model. Maybe you need to specify --display-as?") + G_LOGGER.critical("Could not determine how to display this model. Maybe you need to specify --display-as?") func(args) - def inspect_trt(self, args): if self.arg_groups[ModelArgs].model_type == "engine": if args.mode != "none": @@ -92,13 +105,11 @@ class Model(Tool): network_str = trt_util.str_from_network(network, mode=args.mode).strip() G_LOGGER.info("==== TensorRT Network ====\n{:}".format(network_str)) - def inspect_onnx(self, args): onnx_model = self.arg_groups[OnnxLoaderArgs].load_onnx() model_str = onnx_util.str_from_onnx(onnx_model, mode=args.mode).strip() G_LOGGER.info("==== ONNX Model ====\n{:}".format(model_str)) - def inspect_tf(self, args): tf_graph, _ = self.arg_groups[TfLoaderArgs].load_graph() graph_str = tf_util.str_from_graph(tf_graph, mode=args.mode).strip() diff --git a/tools/Polygraphy/polygraphy/tools/inspect/subtool/tactics.py b/tools/Polygraphy/polygraphy/tools/inspect/subtool/tactics.py index 602e1d77..5438f56f 100644 --- a/tools/Polygraphy/polygraphy/tools/inspect/subtool/tactics.py +++ b/tools/Polygraphy/polygraphy/tools/inspect/subtool/tactics.py @@ -19,19 +19,19 @@ from polygraphy.tools.base import Tool algorithm_selector = mod.lazy_import("polygraphy.backend.trt.algorithm_selector") + class Tactics(Tool): """ Display the contents of tactic replay files in a human readable format. (for example, those generated by `--save-tactics` from `polygraphy run`) """ + def __init__(self): super().__init__("tactics") - def add_parser_args(self, parser): parser.add_argument("tactic_replay", help="Path to a tactic replay file") - def run(self, args): replay = algorithm_selector.TacticReplayData.load(args.tactic_replay) G_LOGGER.info(replay) diff --git a/tools/Polygraphy/polygraphy/tools/registry.py b/tools/Polygraphy/polygraphy/tools/registry.py index 376d5d3c..8d4899cc 100644 --- a/tools/Polygraphy/polygraphy/tools/registry.py +++ b/tools/Polygraphy/polygraphy/tools/registry.py @@ -27,11 +27,14 @@ class MissingTool(Tool): self.err = err # NOTE: When modifying this error message, make sure to update the checks in # tests/test_public_imports.py so that we don't miss errors! - self.__doc__ = "[!] This tool could not be loaded due to an error:\n{:}\nRun 'polygraphy {:}' for details.".format(self.err, self.name) - + self.__doc__ = ( + "[!] This tool could not be loaded due to an error:\n{:}\nRun 'polygraphy {:}' for details.".format( + self.err, self.name + ) + ) def __call__(self, args): - G_LOGGER.exit("Encountered an error when loading this tool:\n{:}".format(self.err)) + G_LOGGER.critical("Encountered an error when loading this tool:\n{:}".format(self.err)) def try_register_tool(module, tool_class): @@ -55,6 +58,6 @@ try_register_tool("polygraphy.tools.to_json", "ToJSON") # Check that tool names are unique tool_names = [tool.name for tool in TOOL_REGISTRY] -duplicates = set([name for name in tool_names if tool_names.count(name) > 1]) +duplicates = {name for name in tool_names if tool_names.count(name) > 1} if duplicates: G_LOGGER.internal_error("Multiple tools have the same name. Duplicate tool names found: {:}".format(duplicates)) diff --git a/tools/Polygraphy/polygraphy/tools/run/run.py b/tools/Polygraphy/polygraphy/tools/run/run.py index 49a295dc..7c672d90 100644 --- a/tools/Polygraphy/polygraphy/tools/run/run.py +++ b/tools/Polygraphy/polygraphy/tools/run/run.py @@ -17,15 +17,28 @@ import argparse import copy from polygraphy.logger import G_LOGGER -from polygraphy.tools.args import (ComparatorCompareArgs, ComparatorRunArgs, - DataLoaderArgs, LoggerArgs, ModelArgs, - OnnxLoaderArgs, OnnxrtRunnerArgs, - OnnxSaveArgs, OnnxShapeInferenceArgs, - Tf2OnnxLoaderArgs, TfConfigArgs, - TfLoaderArgs, TfRunnerArgs, TrtConfigArgs, - TrtEngineLoaderArgs, TrtEngineSaveArgs, - TrtLegacyArgs, TrtNetworkLoaderArgs, - TrtPluginLoaderArgs, TrtRunnerArgs) +from polygraphy.tools.args import ( + ComparatorCompareArgs, + ComparatorRunArgs, + DataLoaderArgs, + LoggerArgs, + ModelArgs, + OnnxLoaderArgs, + OnnxrtRunnerArgs, + OnnxSaveArgs, + OnnxShapeInferenceArgs, + Tf2OnnxLoaderArgs, + TfConfigArgs, + TfLoaderArgs, + TfRunnerArgs, + TrtConfigArgs, + TrtEngineLoaderArgs, + TrtEngineSaveArgs, + TrtLegacyArgs, + TrtNetworkLoaderArgs, + TrtPluginLoaderArgs, + TrtRunnerArgs, +) from polygraphy.tools.base import Tool from polygraphy.tools.script import Script, inline, safe @@ -33,18 +46,23 @@ from polygraphy.tools.script import Script, inline, safe # FIXME: This should be moved into tools/args/ def add_runner_args(parser): class StoreRunnerOrdered(argparse.Action): - def __call__(self, parser, namespace, values, option_string=None): + def __call__(self, parser, namespace, values, option_string=None): if not hasattr(namespace, "runners"): namespace.runners = [] namespace.runners.append(option_string.lstrip("-").replace("-", "_")) - runner_args = parser.add_argument_group("Runners", "Options for selecting runners. Zero or more runners may be specified") + runner_args = parser.add_argument_group( + "Runners", "Options for selecting runners. Zero or more runners may be specified" + ) def add_runner(option, help): runner_args.add_argument(option, help=help, action=StoreRunnerOrdered, dest="runners", default=[], nargs=0) add_runner("--trt", help="Run inference using TensorRT") - add_runner("--trt-legacy", help="Run inference using Legacy TensorRT Runner. Only supports networks using implicit batch mode") + add_runner( + "--trt-legacy", + help="Run inference using Legacy TensorRT Runner. Only supports networks using implicit batch mode", + ) add_runner("--tf", help="Run inference using TensorFlow") add_runner("--onnxrt", help="Run inference using ONNX Runtime") @@ -91,6 +109,7 @@ class Run(Tool): """ Run inference and compare results across backends. """ + def __init__(self): super().__init__("run") self.subscribe_args(ModelArgs()) @@ -113,19 +132,24 @@ class Run(Tool): self.subscribe_args(ComparatorRunArgs()) self.subscribe_args(ComparatorCompareArgs()) - def add_parser_args(self, parser): - parser.add_argument("--gen", "--gen-script", help="Path to save a generated Python script, that will do exactly " - "what `run` would. When this option is enabled, `run` will just save the script and exit. " - "Use `-` to print the script to the standard output", - type=argparse.FileType("w"), dest="gen_script") + parser.add_argument( + "--gen", + "--gen-script", + help="Path to save a generated Python script, that will do exactly " + "what `run` would. When this option is enabled, `run` will just save the script and exit. " + "Use `-` to print the script to the standard output", + type=argparse.FileType("w"), + dest="gen_script", + ) add_runner_args(parser) - def run(self, args): if self.arg_groups[ModelArgs].model_file is None and args.runners: - G_LOGGER.exit("One or more runners was specified, but no model file was provided. Make sure you've specified the model path, " - "and also that it's not being consumed as an argument for another parameter") + G_LOGGER.critical( + "One or more runners was specified, but no model file was provided. Make sure you've specified the model path, " + "and also that it's not being consumed as an argument for another parameter" + ) script = self.build_script(args) @@ -134,10 +158,11 @@ class Run(Tool): else: exec(str(script)) - # Generates a script based on command-line arguments def build_script(self, args): - script = Script(summary=generate_summary(self.arg_groups[ModelArgs].model_file, args.runners, args.load_results)) + script = Script( + summary=generate_summary(self.arg_groups[ModelArgs].model_file, args.runners, args.load_results) + ) self.arg_groups[LoggerArgs].add_to_script(script) @@ -159,12 +184,15 @@ class Run(Tool): script.add_import(imports=["sys"]) cmd_run = inline(safe("' '.join(sys.argv)")) - exit_status = safe('# Report Results\n' - 'cmd_run = {cmd}\n' - 'if not {success}:\n' - '\tG_LOGGER.exit("FAILED | Command: {{}}".format(cmd_run))\n' - 'G_LOGGER.finish("PASSED | Command: {{}}".format(cmd_run))\n', - cmd=cmd_run, success=SUCCESS_VAR_NAME) + exit_status = safe( + "# Report Results\n" + "cmd_run = {cmd}\n" + "if not {success}:\n" + '\tG_LOGGER.critical("FAILED | Command: {{}}".format(cmd_run))\n' + 'G_LOGGER.finish("PASSED | Command: {{}}".format(cmd_run))\n', + cmd=cmd_run, + success=SUCCESS_VAR_NAME, + ) script.append_suffix(exit_status) return script diff --git a/tools/Polygraphy/polygraphy/tools/script.py b/tools/Polygraphy/polygraphy/tools/script.py index 87a64cd7..2e1e6475 100644 --- a/tools/Polygraphy/polygraphy/tools/script.py +++ b/tools/Polygraphy/polygraphy/tools/script.py @@ -30,8 +30,10 @@ def assert_identifier(inp): Raises a PolygraphyException if it can't. """ if not inp.isidentifier(): - G_LOGGER.exit("This argument must be a valid identifier. " - "Provided argument cannot be a Python identifier: {:}".format(inp)) + G_LOGGER.critical( + "This argument must be a valid identifier. " + "Provided argument cannot be a Python identifier: {:}".format(inp) + ) return inp @@ -59,12 +61,12 @@ def ensure_safe(inp): """ Ensures that the input is marked as a safe string (i.e. Script.String(safe=True)). """ - if config.INTERNAL_CORRECTNESS_CHECKS: - if not isinstance(inp, Script.String): - G_LOGGER.internal_error("Input to ensure_safe must be of type Script.String, but was: {:}".format(inp)) - elif not inp.safe: - G_LOGGER.internal_error("Input string: {:} was not checked for safety. " - "This is a potential security risk!".format(inp)) + if not isinstance(inp, Script.String): + G_LOGGER.internal_error("Input to ensure_safe must be of type Script.String, but was: {:}".format(inp)) + elif not inp.safe: + G_LOGGER.internal_error( + "Input string: {:} was not checked for safety. " "This is a potential security risk!".format(inp) + ) return inp @@ -146,9 +148,11 @@ def make_invocable_if_nondefault(type_str, *args, **kwargs): return None return obj_str + ################################# SCRIPT ################################## # Used to generate a script that uses the Polygraphy API. + class Script(object): class String(object): """ @@ -157,16 +161,15 @@ class Script(object): This can be spoofed easily - the purpose is to check Polygraphy's implementations, not external ones. """ + def __init__(self, s, safe=False, inline=False): self.s = s self.safe = safe self.inline = inline - def __str__(self): return str(self.s) - def __repr__(self): if self.inline: # Since only safe strings can be marked inline, self.safe is always @@ -174,24 +177,21 @@ class Script(object): return str(self.s) return repr(self.s) - def __iadd__(self, other): - if config.INTERNAL_CORRECTNESS_CHECKS: - if not isinstance(other, Script.String): - G_LOGGER.critical("Cannot concatenate str and Script.String. Note: str was: {:}".format(other)) - elif self.safe != other.safe: - G_LOGGER.critical("Cannot concatenate unsafe string ({:}) to safe string ({:})!".format(other, self.s)) + if not isinstance(other, Script.String): + G_LOGGER.internal_error("Cannot concatenate str and Script.String. Note: str was: {:}".format(other)) + elif self.safe != other.safe: + G_LOGGER.internal_error( + "Cannot concatenate unsafe string ({:}) to safe string ({:})!".format(other, self.s) + ) self.s += other.s return self - def unwrap(self): return self.s - DATA_LOADER_NAME = String("data_loader", safe=True, inline=True) - def __init__(self, summary=None, always_create_runners=True): """ Represents a Python script that uses the Polygraphy API. @@ -203,17 +203,16 @@ class Script(object): Whether to create the list of runners even if it would be empty. """ self.imports = set() - self.from_imports = defaultdict(set) # Dict[str, List[str]] Maps from module to imported components - self.loaders = OrderedDict() # Dict[str, str] Maps a string constructing a loader to a name. - self.loader_count = defaultdict(int) # Dict[str, int] Maps loader_id to the number of loaders sharing that ID - self.runners = [] # List[str] - self.preimport = [] # List[str] - self.suffix = [] # List[str] - self.data_loader = "" # str Contains the DataLoader constructor + self.from_imports = defaultdict(set) # Dict[str, List[str]] Maps from module to imported components + self.loaders = OrderedDict() # Dict[str, str] Maps a string constructing a loader to a name. + self.loader_count = defaultdict(int) # Dict[str, int] Maps loader_id to the number of loaders sharing that ID + self.runners = [] # List[str] + self.preimport = [] # List[str] + self.suffix = [] # List[str] + self.data_loader = "" # str Contains the DataLoader constructor self.summary = summary self.always_create_runners = always_create_runners - def add_import(self, imports, frm=None): """ Adds imports to this script @@ -227,7 +226,6 @@ class Script(object): else: self.imports.update(imports) - def set_data_loader(self, data_loader_str): """ Adds a data loader to this script, overwriting @@ -248,7 +246,6 @@ class Script(object): self.data_loader = data_loader_str return Script.DATA_LOADER_NAME - def add_loader(self, loader_str, loader_id, suffix=None): """ Adds a loader to the script. @@ -279,11 +276,9 @@ class Script(object): self.loaders[loader_str] = unique_name return unique_name - def get_runners(self): return Script.String("runners", safe=True, inline=True) - def add_runner(self, runner_str): """ Adds a runner to the script. @@ -294,7 +289,6 @@ class Script(object): runner_str = ensure_safe(runner_str).unwrap() self.runners.append(runner_str) - def append_preimport(self, line): """ Append a line to the pre-import prefix of the script. @@ -305,7 +299,6 @@ class Script(object): line = ensure_safe(line).unwrap() self.preimport.append(line) - def append_suffix(self, line): """ Append a line to the suffix of the script @@ -316,11 +309,11 @@ class Script(object): line = ensure_safe(line).unwrap() self.suffix.append(line) - def __str__(self): script = "#!/usr/bin/env python3\n" script += "# Template auto-generated by polygraphy [v{:}] on {:} at {:}\n".format( - polygraphy.__version__, time.strftime("%D"), time.strftime("%H:%M:%S")) + polygraphy.__version__, time.strftime("%D"), time.strftime("%H:%M:%S") + ) script += "# Generation Command: {:}\n".format(" ".join(sys.argv)) if self.summary: script += "# " + "\n# ".join(self.summary.splitlines()) + "\n" @@ -360,7 +353,6 @@ class Script(object): G_LOGGER.super_verbose("Created script:\n{:}".format(script)) return script - def save(self, dest): """ Save this script to the specified destination. diff --git a/tools/Polygraphy/polygraphy/tools/surgeon/subtool/base.py b/tools/Polygraphy/polygraphy/tools/surgeon/subtool/base.py index 98277078..ff32a173 100644 --- a/tools/Polygraphy/polygraphy/tools/surgeon/subtool/base.py +++ b/tools/Polygraphy/polygraphy/tools/surgeon/subtool/base.py @@ -27,30 +27,25 @@ class BaseSurgeonSubtool(Tool): def __init__(self, name): super().__init__(name) - def load_model(self, log_model=True): model = self.arg_groups[OnnxLoaderArgs].load_onnx() if log_model: G_LOGGER.info("Original Model:\n{:}\n\n".format(onnx_util.str_from_onnx(model, mode="none"))) return model - # Since new graph outputs may be added, and we don't know the types, # we skip type checks in ONNX-GraphSurgeon. def export_graph(self, graph, do_type_check=False): return gs.export_onnx(graph, do_type_check=do_type_check) - def save_model(self, model, log_model=True): model = self.arg_groups[OnnxSaveArgs].save_onnx(model) if log_model: G_LOGGER.info("New Model:\n{:}\n\n".format(onnx_util.str_from_onnx(model, mode="none"))) - def run_impl(self, args): raise NotImplementedError("Subclasses must implement run_impl!") - def run(self, args): def set_onnx_gs_logging_level(sev): ONNX_GS_LOGGER = gs.logger.G_LOGGER diff --git a/tools/Polygraphy/polygraphy/tools/surgeon/subtool/extract.py b/tools/Polygraphy/polygraphy/tools/surgeon/subtool/extract.py index 3d55273c..3695e9ef 100644 --- a/tools/Polygraphy/polygraphy/tools/surgeon/subtool/extract.py +++ b/tools/Polygraphy/polygraphy/tools/surgeon/subtool/extract.py @@ -19,8 +19,7 @@ from polygraphy import mod from polygraphy.common import TensorMetadata from polygraphy.logger import G_LOGGER from polygraphy.tools import util as tools_util -from polygraphy.tools.args import (DataLoaderArgs, ModelArgs, OnnxLoaderArgs, - OnnxSaveArgs, OnnxShapeInferenceArgs) +from polygraphy.tools.args import DataLoaderArgs, ModelArgs, OnnxLoaderArgs, OnnxSaveArgs, OnnxShapeInferenceArgs from polygraphy.tools.args import util as args_util from polygraphy.tools.surgeon.subtool.base import BaseSurgeonSubtool @@ -32,6 +31,7 @@ class Extract(BaseSurgeonSubtool): """ Extract a subgraph based on the specified inputs and outputs. """ + def __init__(self): super().__init__("extract") self.subscribe_args(ModelArgs(model_required=True, inputs="--model-inputs", model_type="onnx")) @@ -40,25 +40,33 @@ class Extract(BaseSurgeonSubtool): self.subscribe_args(OnnxLoaderArgs(output_prefix=None)) self.subscribe_args(OnnxSaveArgs(required=True)) - def add_parser_args(self, parser): - parser.add_argument("--inputs", dest="input_meta", help="Input metadata for subgraph (names, shapes, and data types). " - "Use 'auto' to make `extract` determine these automatically. Format: " - "--inputs ::. " - "For example: --inputs input0:[1,3,224,224]:float32 input1:auto:auto. " - "If omitted, uses the current model inputs. ", - nargs="+", default=[]) + parser.add_argument( + "--inputs", + dest="input_meta", + help="Input metadata for subgraph (names, shapes, and data types). " + "Use 'auto' to make `extract` determine these automatically. Format: " + "--inputs ::. " + "For example: --inputs input0:[1,3,224,224]:float32 input1:auto:auto. " + "If omitted, uses the current model inputs. ", + nargs="+", + default=[], + ) - parser.add_argument("--outputs", dest="output_meta", help="Output metadata for subgraph (names and data types). " - "Use 'auto' to make `extract` determine these automatically. Format: " - "--outputs :. " - "For example: --outputs output0:float32 output1:auto. " - "If omitted, uses the current model outputs. ", - nargs="+", default=[]) + parser.add_argument( + "--outputs", + dest="output_meta", + help="Output metadata for subgraph (names and data types). " + "Use 'auto' to make `extract` determine these automatically. Format: " + "--outputs :. " + "For example: --outputs output0:float32 output1:auto. " + "If omitted, uses the current model outputs. ", + nargs="+", + default=[], + ) super().add_parser_args(parser) - def run_impl(self, args): def missing_meta_tensors(input_metadata, output_metadata): missing = TensorMetadata() @@ -70,7 +78,6 @@ class Extract(BaseSurgeonSubtool): missing.add(name, dtype, shape) return missing - model = super().load_model() user_input_metadata = args_util.parse_meta(args.input_meta) @@ -83,7 +90,7 @@ class Extract(BaseSurgeonSubtool): def get_tensor(name): if name not in TENSOR_MAP: - G_LOGGER.exit("Tensor: {:} does not exist in the model.".format(name)) + G_LOGGER.critical("Tensor: {:} does not exist in the model.".format(name)) return TENSOR_MAP[name] # Makes a TensorMetadata for inputs/outputs using either the user provided information @@ -102,28 +109,32 @@ class Extract(BaseSurgeonSubtool): output_metadata = make_io_meta(user_output_metadata, graph.outputs) return graph, input_metadata, output_metadata - graph, input_metadata, output_metadata = load_graph_and_io_meta(model) # If we've already done ONNX shape inference, we should not do it again here. - skip_shape_inference = self.arg_groups[OnnxShapeInferenceArgs].force_fallback or self.arg_groups[OnnxShapeInferenceArgs].do_shape_inference + skip_shape_inference = ( + self.arg_groups[OnnxShapeInferenceArgs].force_fallback + or self.arg_groups[OnnxShapeInferenceArgs].do_shape_inference + ) if missing_meta_tensors(input_metadata, output_metadata) and not skip_shape_inference: - G_LOGGER.info("Running ONNX shape inference to derive shapes and/or data types for `auto` arguments.\n" - "To avoid this, you can specify the shapes and data types explicitly.") + G_LOGGER.info( + "Running ONNX shape inference to derive shapes and/or data types for `auto` arguments.\n" + "To avoid this, you can specify the shapes and data types explicitly." + ) model = onnx_backend.infer_shapes(model) graph, input_metadata, output_metadata = load_graph_and_io_meta(model) - missing_tensors = missing_meta_tensors(input_metadata, output_metadata) if missing_tensors or self.arg_groups[OnnxShapeInferenceArgs].force_fallback: # Use ONNX runtime with static shapes to infer shapes when all else fails # Returns a TensorMetadata for all tensors in the graph. if not self.arg_groups[OnnxShapeInferenceArgs].force_fallback: - G_LOGGER.warning("Some tensor shapes or dtypes are missing in the model. Note: Tensors with missing information:\n{:}\n" - "Will run inference to determine shapes. This may cause some dynamic " - "dimensions to become static.\n" - "To avoid this, please provide metadata on the command-line. " - .format(missing_tensors)) + G_LOGGER.warning( + "Some tensor shapes or dtypes are missing in the model. Note: Tensors with missing information:\n{:}\n" + "Will run inference to determine shapes. This may cause some dynamic " + "dimensions to become static.\n" + "To avoid this, please provide metadata on the command-line. ".format(missing_tensors) + ) else: G_LOGGER.info("Forcing fallback shape inference. This will cause dynamic dimensions to become static.") @@ -150,9 +161,9 @@ class Extract(BaseSurgeonSubtool): return meta input_metadata = update_meta_from_layerwise(input_metadata, user_input_metadata) - output_metadata = update_meta_from_layerwise(output_metadata, user_output_metadata, - set_shapes=self.arg_groups[OnnxShapeInferenceArgs].force_fallback) - + output_metadata = update_meta_from_layerwise( + output_metadata, user_output_metadata, set_shapes=self.arg_groups[OnnxShapeInferenceArgs].force_fallback + ) graph = onnx_backend.extract_subgraph(graph, input_metadata, output_metadata) super().save_model(super().export_graph(graph)) diff --git a/tools/Polygraphy/polygraphy/tools/surgeon/subtool/insert.py b/tools/Polygraphy/polygraphy/tools/surgeon/subtool/insert.py index a8cd45ba..13b4b90d 100644 --- a/tools/Polygraphy/polygraphy/tools/surgeon/subtool/insert.py +++ b/tools/Polygraphy/polygraphy/tools/surgeon/subtool/insert.py @@ -15,8 +15,7 @@ # from polygraphy import mod from polygraphy.logger import G_LOGGER -from polygraphy.tools.args import (DataLoaderArgs, ModelArgs, OnnxLoaderArgs, - OnnxSaveArgs, OnnxShapeInferenceArgs) +from polygraphy.tools.args import DataLoaderArgs, ModelArgs, OnnxLoaderArgs, OnnxSaveArgs, OnnxShapeInferenceArgs from polygraphy.tools.args import util as args_util from polygraphy.tools.args.base import BaseArgs from polygraphy.tools.surgeon.subtool.base import BaseSurgeonSubtool @@ -27,20 +26,34 @@ gs = mod.lazy_import("onnx_graphsurgeon") class OnnxNodeArgs(BaseArgs): def add_to_parser(self, parser): node_args = parser.add_argument_group("Inserted Node", "Options for the node to insert") - node_args.add_argument("--inputs", help="The names of input tensors for the new node. Order will be preserved. " - "Format: --inputs . For example: --inputs name0 name1", nargs="+", required=True) - node_args.add_argument("--outputs", help="The names of output tensors for the new node. Order will be preserved. " - "If an output tensor is also specified as an input, a new tensor will be generated for the output" - "Format: --outputs . For example: --outputs name0 name1", nargs="+", required=True) + node_args.add_argument( + "--inputs", + help="The names of input tensors for the new node. Order will be preserved. " + "Format: --inputs . For example: --inputs name0 name1", + nargs="+", + required=True, + ) + node_args.add_argument( + "--outputs", + help="The names of output tensors for the new node. Order will be preserved. " + "If an output tensor is also specified as an input, a new tensor will be generated for the output" + "Format: --outputs . For example: --outputs name0 name1", + nargs="+", + required=True, + ) node_args.add_argument("--op", help="The ONNX op to use for the new node", required=True) node_args.add_argument("--name", help="The name to use for the new node", default=None) - node_args.add_argument("--attrs", help="Attributes to set in the new node. " - "Format: --attrs =value. For example: --attrs axis=1 keepdims=1. " - "Attributes of type: float, int, str, and lists of these types are supported. " - "Numbers including a decimal point will always be parsed as floats, and quoted values " - "(e.g. --attrs name='53') will always be parsed as strings. Values enclosed in brackets " - "(e.g. --attrs axes=[0,1]) will be parsed as lists. ", - nargs="+", default=[]) + node_args.add_argument( + "--attrs", + help="Attributes to set in the new node. " + "Format: --attrs =value. For example: --attrs axis=1 keepdims=1. " + "Attributes of type: float, int, str, and lists of these types are supported. " + "Numbers including a decimal point will always be parsed as floats, and quoted values " + "(e.g. --attrs name='53') will always be parsed as strings. Values enclosed in brackets " + "(e.g. --attrs axes=[0,1]) will be parsed as lists. ", + nargs="+", + default=[], + ) def parse(self, args): self.op = args_util.get(args, "op") @@ -56,6 +69,7 @@ class Insert(BaseSurgeonSubtool): [EXPERIMENTAL] Insert a single node into a graph with the specified inputs and outputs. Any existing subgraph between the inputs and outputs is replaced. """ + def __init__(self): super().__init__("insert") self.subscribe_args(OnnxNodeArgs()) @@ -64,7 +78,6 @@ class Insert(BaseSurgeonSubtool): self.subscribe_args(OnnxLoaderArgs(output_prefix=None)) self.subscribe_args(OnnxSaveArgs(infer_shapes=True, required=True)) - def run_impl(self, args): graph = gs.import_onnx(super().load_model()) @@ -72,10 +85,9 @@ class Insert(BaseSurgeonSubtool): def get_tensor(name): if name not in TENSOR_MAP: - G_LOGGER.exit("Tensor: {:} does not exist in the model.".format(name)) + G_LOGGER.critical("Tensor: {:} does not exist in the model.".format(name)) return TENSOR_MAP[name] - TENSOR_NAME_SUFFIX = "_polygraphy_surgeon_insert_output" output_tensors = [] @@ -108,8 +120,13 @@ class Insert(BaseSurgeonSubtool): input_tensors = [get_tensor(name) for name in self.arg_groups[OnnxNodeArgs].inputs] - new_node = gs.Node(op=self.arg_groups[OnnxNodeArgs].op, name=self.arg_groups[OnnxNodeArgs].name, - attrs=self.arg_groups[OnnxNodeArgs].attrs, inputs=input_tensors, outputs=output_tensors) + new_node = gs.Node( + op=self.arg_groups[OnnxNodeArgs].op, + name=self.arg_groups[OnnxNodeArgs].name, + attrs=self.arg_groups[OnnxNodeArgs].attrs, + inputs=input_tensors, + outputs=output_tensors, + ) G_LOGGER.verbose("Generated new node: {:}".format(new_node)) # Assuming the graph is topologically sorted, the node needs to be inserted diff --git a/tools/Polygraphy/polygraphy/tools/surgeon/subtool/sanitize.py b/tools/Polygraphy/polygraphy/tools/surgeon/subtool/sanitize.py index 8c5ca245..2bc9f1a9 100644 --- a/tools/Polygraphy/polygraphy/tools/surgeon/subtool/sanitize.py +++ b/tools/Polygraphy/polygraphy/tools/surgeon/subtool/sanitize.py @@ -15,8 +15,7 @@ # from polygraphy import mod from polygraphy.tools import util as tools_util -from polygraphy.tools.args import (DataLoaderArgs, ModelArgs, OnnxLoaderArgs, - OnnxSaveArgs, OnnxShapeInferenceArgs) +from polygraphy.tools.args import DataLoaderArgs, ModelArgs, OnnxLoaderArgs, OnnxSaveArgs, OnnxShapeInferenceArgs from polygraphy.tools.surgeon.subtool.base import BaseSurgeonSubtool onnx_backend = mod.lazy_import("polygraphy.backend.onnx") @@ -27,6 +26,7 @@ class Sanitize(BaseSurgeonSubtool): """ Clean up and optimize an ONNX model. """ + def __init__(self): super().__init__("sanitize") self.subscribe_args(ModelArgs(model_required=True, inputs="--override-inputs", model_type="onnx")) @@ -35,33 +35,63 @@ class Sanitize(BaseSurgeonSubtool): self.subscribe_args(OnnxLoaderArgs(output_prefix="")) self.subscribe_args(OnnxSaveArgs(infer_shapes=True, required=True)) - def add_parser_args(self, parser): const_fold_args = parser.add_argument_group("Constant Folding", "Options for folding constants") - const_fold_args.add_argument("--fold-constants", help="Fold constants in the graph by computing subgraphs whose values " - "are not dependent on runtime inputs.", action="store_true", default=None) - const_fold_args.add_argument("--num-passes", "--num-const-fold-passes", help="The number of constant folding passes to run. " - "Sometimes, subgraphs that compute tensor shapes may not be foldable in a single pass. " - "If not specified, Polygraphy will automatically determine the number of passes required. ", - type=int, default=None, dest="num_const_fold_passes") - const_fold_args.add_argument("--partitioning", help="Controls how to partition the graph during constant folding: {{" - "'basic': Partition the graph so failures in one part do not affect other parts, " - "'recursive': In addition to partitioning the graph, partition partitions where needed}} ", - choices=["basic", "recursive"], default=None) - const_fold_args.add_argument("--no-fold-shapes", help="Disable folding Shape nodes and subgraphs that operate on shapes", - dest="fold_shapes", default=True, action="store_false") + const_fold_args.add_argument( + "--fold-constants", + help="Fold constants in the graph by computing subgraphs whose values " + "are not dependent on runtime inputs.", + action="store_true", + default=None, + ) + const_fold_args.add_argument( + "--num-passes", + "--num-const-fold-passes", + help="The number of constant folding passes to run. " + "Sometimes, subgraphs that compute tensor shapes may not be foldable in a single pass. " + "If not specified, Polygraphy will automatically determine the number of passes required. ", + type=int, + default=None, + dest="num_const_fold_passes", + ) + const_fold_args.add_argument( + "--partitioning", + help="Controls how to partition the graph during constant folding: {{" + "'basic': Partition the graph so failures in one part do not affect other parts, " + "'recursive': In addition to partitioning the graph, partition partitions where needed}} ", + choices=["basic", "recursive"], + default=None, + ) + const_fold_args.add_argument( + "--no-fold-shapes", + help="Disable folding Shape nodes and subgraphs that operate on shapes", + dest="fold_shapes", + default=True, + action="store_false", + ) + const_fold_args.add_argument( + "--no-per-pass-shape-inference", + help="Disable shape inference between passes of constant folding", + dest="per_pass_shape_inference", + default=True, + action="store_false", + ) - parser.add_argument("--cleanup", help="Run dead layer removal on the graph. This is generally not required if other options are set. ", - action="store_true", default=False) + parser.add_argument( + "--cleanup", + help="Run dead layer removal on the graph. This is generally not required if other options are set. ", + action="store_true", + default=False, + ) super().add_parser_args(parser) - def run_impl(self, args): # First do all processing that requires an ONNX-GraphSurgeon graph, then do everything # that operates on the ONNX model. This lets us avoid ONNX-GraphSurgeon import if we don't # need it. def do_graph_processing(model): graph = None + rerun_shape_inference = False def get_graph(): nonlocal graph @@ -69,11 +99,11 @@ class Sanitize(BaseSurgeonSubtool): graph = gs.import_onnx(model) return graph - user_input_metadata = self.arg_groups[ModelArgs].input_shapes if user_input_metadata: graph = get_graph() graph = tools_util.override_input_shapes(graph, user_input_metadata) + rerun_shape_inference = True if self.arg_groups[OnnxShapeInferenceArgs].force_fallback: _, layerwise_meta = self.arg_groups[OnnxShapeInferenceArgs].fallback_inference(model) @@ -86,18 +116,26 @@ class Sanitize(BaseSurgeonSubtool): if graph is not None: model = gs.export_onnx(graph) - return model - + return model, rerun_shape_inference def do_model_processing(model): if args.fold_constants: - model = onnx_backend.fold_constants(model, num_passes=args.num_const_fold_passes, - do_shape_inference=self.arg_groups[OnnxShapeInferenceArgs].do_shape_inference, - fold_shapes=args.fold_shapes, partitioning=args.partitioning) + model = onnx_backend.fold_constants( + model, + num_passes=args.num_const_fold_passes, + do_shape_inference=self.arg_groups[OnnxShapeInferenceArgs].do_shape_inference + if args.per_pass_shape_inference + else False, + fold_shapes=args.fold_shapes, + partitioning=args.partitioning, + ) return model - model = super().load_model() - model = do_graph_processing(model) + model, rerun_shape_inference = do_graph_processing(model) + + if rerun_shape_inference and self.arg_groups[OnnxShapeInferenceArgs].do_shape_inference: + model = onnx_backend.infer_shapes(model) + model = do_model_processing(model) super().save_model(model) diff --git a/tools/Polygraphy/polygraphy/tools/surgeon/surgeon.py b/tools/Polygraphy/polygraphy/tools/surgeon/surgeon.py index 5614fd6f..a4a05446 100644 --- a/tools/Polygraphy/polygraphy/tools/surgeon/surgeon.py +++ b/tools/Polygraphy/polygraphy/tools/surgeon/surgeon.py @@ -18,14 +18,15 @@ from polygraphy.tools.surgeon.subtool import Extract, Insert, Sanitize ################################# MAIN TOOL ################################# + class Surgeon(Tool): """ Modify ONNX models. """ + def __init__(self): super().__init__("surgeon") - def add_parser_args(self, parser): subparsers = parser.add_subparsers(title="Surgical Instruments", dest="instrument") subparsers.required = True diff --git a/tools/Polygraphy/polygraphy/tools/template/subtool/trt_network.py b/tools/Polygraphy/polygraphy/tools/template/subtool/trt_network.py index c42bf76b..0c32b9f8 100644 --- a/tools/Polygraphy/polygraphy/tools/template/subtool/trt_network.py +++ b/tools/Polygraphy/polygraphy/tools/template/subtool/trt_network.py @@ -15,9 +15,14 @@ # import argparse -from polygraphy.tools.args import (ModelArgs, OnnxLoaderArgs, - Tf2OnnxLoaderArgs, TfLoaderArgs, - TrtNetworkLoaderArgs, TrtPluginLoaderArgs) +from polygraphy.tools.args import ( + ModelArgs, + OnnxLoaderArgs, + Tf2OnnxLoaderArgs, + TfLoaderArgs, + TrtNetworkLoaderArgs, + TrtPluginLoaderArgs, +) from polygraphy.tools.base import Tool from polygraphy.tools.script import Script, inline, safe @@ -27,6 +32,7 @@ class TrtNetwork(Tool): Generate a template script that defines or modifies a TensorRT network using the TensorRT network API. """ + def __init__(self): super().__init__("trt-network") self.subscribe_args(ModelArgs(model_required=False, inputs=None)) @@ -36,14 +42,15 @@ class TrtNetwork(Tool): self.subscribe_args(TrtPluginLoaderArgs()) self.subscribe_args(TrtNetworkLoaderArgs()) - def add_parser_args(self, parser): - parser.add_argument("-o", "--output", help="Path to save the generated script.", - type=argparse.FileType("w"), required=True) - + parser.add_argument( + "-o", "--output", help="Path to save the generated script.", type=argparse.FileType("w"), required=True + ) def run(self, args): - script = Script(summary="Defines or modifies a TensorRT Network using the Network API.", always_create_runners=False) + script = Script( + summary="Defines or modifies a TensorRT Network using the Network API.", always_create_runners=False + ) script.add_import(imports=["func"], frm="polygraphy") script.add_import(imports=["tensorrt as trt"]) diff --git a/tools/Polygraphy/polygraphy/tools/template/template.py b/tools/Polygraphy/polygraphy/tools/template/template.py index a7341bee..c2b307f4 100644 --- a/tools/Polygraphy/polygraphy/tools/template/template.py +++ b/tools/Polygraphy/polygraphy/tools/template/template.py @@ -21,10 +21,10 @@ class Template(Tool): """ [EXPERIMENTAL] Generate template files. """ + def __init__(self): super().__init__("template") - def add_parser_args(self, parser): subparsers = parser.add_subparsers(title="Template Subtools", dest="subtool") subparsers.required = True diff --git a/tools/Polygraphy/polygraphy/tools/to_json/to_json.py b/tools/Polygraphy/polygraphy/tools/to_json/to_json.py index 812e14a7..ca530288 100644 --- a/tools/Polygraphy/polygraphy/tools/to_json/to_json.py +++ b/tools/Polygraphy/polygraphy/tools/to_json/to_json.py @@ -24,16 +24,15 @@ class ToJSON(Tool): This tool will be removed in 0.31.0 since all future versions of Polygraphy will not use Pickle for serialization. """ + def __init__(self): mod.warn_deprecated("to-json", use_instead="JSON serialization", remove_in="0.31.0") super().__init__(name="to-json") - def add_parser_args(self, parser): parser.add_argument("pickle_data", help="Path to old pickled data") parser.add_argument("-o", "--output", help="Path at which to write the JSON-ified data.", required=True) - def run(self, args): import pickle diff --git a/tools/Polygraphy/polygraphy/tools/util.py b/tools/Polygraphy/polygraphy/tools/util.py index ad1c3024..264506d3 100644 --- a/tools/Polygraphy/polygraphy/tools/util.py +++ b/tools/Polygraphy/polygraphy/tools/util.py @@ -35,7 +35,9 @@ def meta_from_gs_tensors(tensors): def override_input_shapes(graph, user_input_metadata): """ Overrides input shapes in the model according to the provided input metadata. - Inputs omitted from user_input_metadata are not changed + Inputs omitted from user_input_metadata are not changed. + + Shapes of intermediate tensors are cleared. """ # We can leverage extract_subgraph if we make sure all the current graph inputs are preserved. # We need to be careful to preserve the order of graph inputs here. diff --git a/tools/Polygraphy/polygraphy/util/format.py b/tools/Polygraphy/polygraphy/util/format.py index 8d53548c..8a197b94 100644 --- a/tools/Polygraphy/polygraphy/util/format.py +++ b/tools/Polygraphy/polygraphy/util/format.py @@ -27,6 +27,7 @@ class DataFormat(enum.IntEnum): NHWC = 4 NCHW = 5 + # This class is responsible for deducing the format of a shape, # and converting it to the desired format (specified as a DataFormat). class FormatManager(object): @@ -88,8 +89,11 @@ class FormatManager(object): elif len(shape) == 2: return DataFormat.NW else: - G_LOGGER.warning("Cannot determine format for " + str(shape) + - ". Currently only implemented for input_buffers with 1-3 non-batch dimensions. Please update this function!") + G_LOGGER.warning( + "Cannot determine format for " + + str(shape) + + ". Currently only implemented for input_buffers with 1-3 non-batch dimensions. Please update this function!" + ) return DataFormat.UNKNOWN # Get the permutation required to transpose old_format to new_format diff --git a/tools/Polygraphy/polygraphy/util/util.py b/tools/Polygraphy/polygraphy/util/util.py index 6003e6b6..b577f00b 100644 --- a/tools/Polygraphy/polygraphy/util/util.py +++ b/tools/Polygraphy/polygraphy/util/util.py @@ -23,9 +23,24 @@ from polygraphy import constants, mod from polygraphy.logger import G_LOGGER np = mod.lazy_import("numpy") -fmt = mod.lazy_import("polygraphy.util.format") -mod.export_deprecated_alias("misc", remove_in="0.30.0", use_instead="polygraphy.util")(sys.modules[__name__]) +mod.export_deprecated_alias("misc", remove_in="0.32.0", use_instead="polygraphy.util")(sys.modules[__name__]) + + +@mod.export() +def check(cond, msg=None): + """ + Like assert, but applies even when optimizations are enabled (i.e. __debug__ is False). + + Args: + cond (bool): The condition to check. + msg (str): The error message in case condition is False. + + Raises: + AssertionError: If the condition is False. + """ + if not cond: + raise AssertionError(msg) @mod.export() @@ -88,14 +103,20 @@ def check_dict_contains(dct, keys, check_missing=True, dict_name=None, log_func= extra_in_dct = feed_names - keys if missing_in_dct: - log_func("Some keys are missing in {:}: {:}.\n" - "Note: Expected keys are: {:}, but keys provided were: {:}".format( - dict_name, missing_in_dct, keys, feed_names)) + log_func( + "Some keys are missing in {:}: {:}.\n" + "Note: Expected keys are: {:}, but keys provided were: {:}".format( + dict_name, missing_in_dct, keys, feed_names + ) + ) if extra_in_dct: - log_func("Extra keys in {:}: {:}.\n" - "Note: Expected keys are: {:}, but keys provided were: {:}".format( - dict_name, extra_in_dct, keys, feed_names)) + log_func( + "Extra keys in {:}: {:}.\n" + "Note: Expected keys are: {:}, but keys provided were: {:}".format( + dict_name, extra_in_dct, keys, feed_names + ) + ) return not extra_in_dct and not missing_in_dct @@ -142,7 +163,7 @@ def unique_list(sequence): # >>> y = MyClass() # >>> y.value # [] -@mod.export_deprecated_alias("default_value", remove_in="0.30.0") +@mod.export_deprecated_alias("default_value", remove_in="0.32.0") @mod.export() def default(value, default): """ @@ -176,8 +197,8 @@ def unpack_args(args, num): Returns: Tuple[object]: A tuple containing `num` arguments, padded with `None` if `len(args) < num` """ - args = args if is_sequence(args) else (args, ) - args += (None, ) * (num - len(args)) + args = args if is_sequence(args) else (args,) + args += (None,) * (num - len(args)) return args[0:num] @@ -185,6 +206,7 @@ def unpack_args(args, num): ## File I/O ## + @mod.export() def get_file_size(src): """ @@ -209,7 +231,7 @@ def get_file_size(src): return os.stat(path).st_size -def check_mode(file_like, mode): +def warn_if_wrong_mode(file_like, mode): def binary(mode): return "b" in mode @@ -220,9 +242,25 @@ def check_mode(file_like, mode): return "w" in mode or "a" in mode or "+" in mode fmode = file_like.mode - if binary(fmode) != binary(mode) or (readable(mode) and not readable(fmode)) or (writable(mode) and not writable(fmode)) : - G_LOGGER.warning("File-like object has a different mode than requested!\n" - "Note: Requested mode was: {:} but file-like object has mode: {:}".format(mode, file_like.mode)) + if ( + binary(fmode) != binary(mode) + or (readable(mode) and not readable(fmode)) + or (writable(mode) and not writable(fmode)) + ): + G_LOGGER.warning( + "File-like object has a different mode than requested!\n" + "Note: Requested mode was: {:} but file-like object has mode: {:}".format(mode, file_like.mode) + ) + + +def is_file_like(obj): + try: + obj.read + obj.write + except AttributeError: + return False + else: + return True @mod.export() @@ -246,8 +284,8 @@ def load_file(src, mode="rb", description=None): if description is not None: G_LOGGER.info("Loading {:} from {:}".format(description, src)) - try: - check_mode(src, mode) + if is_file_like(src): + warn_if_wrong_mode(src, mode) # Reset cursor position after reading from the beginning of the file. prevpos = src.tell() if src.seekable(): @@ -256,12 +294,11 @@ def load_file(src, mode="rb", description=None): if src.seekable(): src.seek(prevpos) return contents - except AttributeError: + else: with open(src, mode) as f: return f.read() - @mod.export() def save_file(contents, dest, mode="wb", description=None): """ @@ -286,8 +323,8 @@ def save_file(contents, dest, mode="wb", description=None): if description is not None: G_LOGGER.info("Saving {:} to {:}".format(description, dest)) - try: - check_mode(dest, mode) + if is_file_like(dest): + warn_if_wrong_mode(dest, mode) bytes_written = dest.write(contents) dest.flush() try: @@ -296,9 +333,11 @@ def save_file(contents, dest, mode="wb", description=None): pass else: if bytes_written != content_bytes: - G_LOGGER.warning("Could not write entire file. Note: file contains {:} bytes, but only " - "{:} bytes were written".format(content_bytes, bytes_written)) - except AttributeError: + G_LOGGER.warning( + "Could not write entire file. Note: file contains {:} bytes, but only " + "{:} bytes were written".format(content_bytes, bytes_written) + ) + else: dir_path = os.path.dirname(dest) if dir_path: dir_path = os.path.realpath(dir_path) @@ -315,10 +354,12 @@ def save_file(contents, dest, mode="wb", description=None): ## Compression ## + class Compressed(object): """ Represents an object compressed by zlib """ + def __init__(self, cobj): self.bytes = cobj @@ -347,10 +388,12 @@ PIPE_MAX_SEND_BYTES = 1 << 31 def send_on_queue(queue, obj): if sys.getsizeof(obj) > PIPE_MAX_SEND_BYTES: - G_LOGGER.warning("Object size ({:} bytes) exceeds maximum size that can be sent over queues ({:} bytes). " - "Attempting to compress - this may take some time. If this does not work or you want to avoid " - "the compression overhead, you should disable subprocesses by omitting the --use-subprocess flag, " - "or by setting use_subprocess=False in Comparator.run().".format(sys.getsizeof(obj), PIPE_MAX_SEND_BYTES)) + G_LOGGER.warning( + "Object size ({:} bytes) exceeds maximum size that can be sent over queues ({:} bytes). " + "Attempting to compress - this may take some time. If this does not work or you want to avoid " + "the compression overhead, you should disable subprocesses by omitting the --use-subprocess flag, " + "or by setting use_subprocess=False in Comparator.run().".format(sys.getsizeof(obj), PIPE_MAX_SEND_BYTES) + ) obj = compress(obj) assert sys.getsizeof(obj) <= PIPE_MAX_SEND_BYTES @@ -390,13 +433,17 @@ def try_receive_on_queue(queue, timeout=None): try: obj = receive_on_queue(queue, timeout) if obj is None: - G_LOGGER.warning("Received {:} on the queue. This likely means that there was an error in sending " - "the object over the queue. You may want to run with use_subprocess=False in Comparator.run() " - "or omit the --use-subprocess flag to prevent further issues.".format(obj)) + G_LOGGER.warning( + "Received {:} on the queue. This likely means that there was an error in sending " + "the object over the queue. You may want to run with use_subprocess=False in Comparator.run() " + "or omit the --use-subprocess flag to prevent further issues.".format(obj) + ) return obj except Exception as err: - G_LOGGER.warning("Could not receive on queue: {:}\nYou may want to run with use_subprocess=False in Comparator.run() " - "or omit the --use-subprocess flag to prevent further issues.".format(err)) + G_LOGGER.warning( + "Could not receive on queue: {:}\nYou may want to run with use_subprocess=False in Comparator.run() " + "or omit the --use-subprocess flag to prevent further issues.".format(err) + ) return None @@ -404,6 +451,7 @@ def try_receive_on_queue(queue, timeout=None): ## Function Utils ## + @mod.export() def invoke_if_callable(func, *args, **kwargs): """ @@ -420,6 +468,7 @@ def invoke_if_callable(func, *args, **kwargs): ## Shapes ## + def is_dimension_dynamic(dim): is_dim_str = not isinstance(dim, int) return dim is None or is_dim_str or dim < 0 @@ -473,29 +522,56 @@ def try_match_shape(arr, shape): Returns: numpy.ndarray: The reshaped array. """ + def is_rank_same(arr, shape): return len(shape) == len(arr.shape) def try_reshape(arr, shape): + original_shape = arr.shape try: arr = arr.reshape(shape) - G_LOGGER.verbose("Reshaped array to shape: {:}".format(arr.shape)) except ValueError: - G_LOGGER.warning("Could not reshape array (shape: {:}) to {:}. Skipping reshape.".format(arr.shape, shape)) + G_LOGGER.warning( + "Could not reshape array from shape: {:} to {:}. Skipping reshape.".format(arr.shape, shape) + ) + else: + if arr.shape != original_shape: + G_LOGGER.info("Reshaped array from shape: {:} to: {:}".format(original_shape, arr.shape)) return arr def try_permute(arr, shape): + original_shape = arr.shape + + if sorted(arr.shape) != sorted(shape): + G_LOGGER.extra_verbose("Array of shape: {:} cannot be permuted to: {:}".format(arr.shape, shape)) + return arr + + # We need to remove axes from the original shape as we use them to avoid + # duplication in the permutation. + arr_shape_indices = {index: dimlen for index, dimlen in enumerate(arr.shape)} + + # Find which axis in arr.shape corresponds to the specified size. Never returns duplicates. + def find_axis(dimlen): + nonlocal arr_shape_indices + for index, d in arr_shape_indices.items(): + if d == dimlen: + del arr_shape_indices[index] + return index + try: - perm = fmt.FormatManager.permutation(fmt.FormatManager.determine_format(arr.shape), fmt.FormatManager.determine_format(shape)) - G_LOGGER.verbose("Permuting shape: {:} using permutation {:}".format(arr.shape, perm)) + perm = [find_axis(dimlen) for dimlen in shape] arr = np.transpose(arr, perm) except Exception as err: - # FormatManager may not recognize the format or be able generate the permutation for the format combination G_LOGGER.extra_verbose("Skipping permutation due to {:}".format(err)) + else: + if arr.shape != original_shape: + G_LOGGER.info( + "Permuted array of shape: {:} to: {:} using permutation {:}".format(original_shape, arr.shape, perm) + ) return arr # Override any dynamic dimensions in the shape with concrete shapes from the array. - def try_fix_shape(arr, shape): + def try_freeze_shape(arr, shape): if num_dynamic_dimensions(shape) == 1: try: static_dims = [dim for dim in shape if not is_dimension_dynamic(dim)] @@ -504,29 +580,23 @@ def try_match_shape(arr, shape): determined_dim = 0 shape = [determined_dim if is_dimension_dynamic(elem) else elem for elem in shape] elif is_rank_same(arr, shape): - shape = [arr_shape_elem if is_dimension_dynamic(elem) else elem for elem, arr_shape_elem in zip(shape, arr.shape)] + shape = [ + arr_shape_elem if is_dimension_dynamic(elem) else elem for elem, arr_shape_elem in zip(shape, arr.shape) + ] return shape if shape == arr.shape: return arr - # When ranks are unequal, we try to squeeze first - if not is_rank_same(arr, shape): - shape = [elem for elem in shape if elem != 1] - arr = np.squeeze(arr) - if is_shape_dynamic(shape): - shape = try_fix_shape(arr, shape) + shape = try_freeze_shape(arr, shape) - # If the rank is still not the same, do a reshape on the second if not is_rank_same(arr, shape): arr = try_reshape(arr, shape) - # Next, permute if the ranks now match if is_rank_same(arr, shape): arr = try_permute(arr, shape) - # Do a final reshape after the outputs have been permuted. arr = try_reshape(arr, shape) return arr @@ -535,6 +605,7 @@ def try_match_shape(arr, shape): ## Logging Utilities ## + @mod.export() def str_from_layer(prefix, index, name, op, input_info, output_info): layer_str = "{:} {:<4} | {:} [Op: {:}]\n".format(prefix, index, name, op) @@ -542,7 +613,9 @@ def str_from_layer(prefix, index, name, op, input_info, output_info): layer_str += "\n" if (input_info and output_info) else "" indent_level = 1 if (input_info and output_info) else 0 - layer_str += indent_block(" -> {:}".format(indent_block(output_info, level=indent_level).strip()), level=indent_level) + "\n" + layer_str += ( + indent_block(" -> {:}".format(indent_block(output_info, level=indent_level).strip()), level=indent_level) + "\n" + ) return layer_str @@ -595,6 +668,7 @@ def make_repr(type_str, *args, **kwargs): ## Safety ## + @mod.export() class FreeOnException(object): def __init__(self, objs): @@ -608,14 +682,12 @@ class FreeOnException(object): assert is_sequence(objs), "FreeOnException requires a sequence of objects!" self.objs = objs - def __enter__(self): """ Returns the objects managed by this context manager. """ return self.objs - def __exit__(self, exc_type, exc_value, traceback): """ On exception, deletes all tracked objects. @@ -635,6 +707,7 @@ class TempAttrChange(object): Temporarily set an instance member to a particular value for the duration of the context manager. """ + def __init__(self, arg_group, attr, value): self.arg_group = arg_group self.attr = attr @@ -642,12 +715,10 @@ class TempAttrChange(object): self.old_value = getattr(arg_group, attr) self.new_value = value - def __enter__(self): if self.new_value is not None: setattr(self.arg_group, self.attr, self.new_value) - def __exit__(self, exc_type, exc_value, traceback): setattr(self.arg_group, self.attr, self.old_value) diff --git a/tools/Polygraphy/setup.py b/tools/Polygraphy/setup.py index 0968f7ec..5bce5a16 100644 --- a/tools/Polygraphy/setup.py +++ b/tools/Polygraphy/setup.py @@ -21,15 +21,17 @@ from setuptools import setup, find_packages ROOT_DIR = os.path.abspath(os.path.dirname(__file__)) BIN_DIR = os.path.join(ROOT_DIR, "bin") + def no_publish(): - blacklist = ['register'] + blacklist = ["register"] for cmd in blacklist: if cmd in sys.argv: - raise RuntimeError("Command \"{}\" blacklisted".format(cmd)) + raise RuntimeError('Command "{}" blacklisted'.format(cmd)) REQUIRED_PACKAGES = [] + def main(): no_publish() setup( @@ -41,8 +43,8 @@ def main(): author="NVIDIA", author_email="svc_tensorrt@nvidia.com", classifiers=[ - 'Intended Audience :: Developers', - 'Programming Language :: Python :: 3', + "Intended Audience :: Developers", + "Programming Language :: Python :: 3", ], license="Apache 2.0", install_requires=REQUIRED_PACKAGES, @@ -51,5 +53,6 @@ def main(): zip_safe=True, ) -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/tools/Polygraphy/tests/backend/common/test_loader.py b/tools/Polygraphy/tests/backend/common/test_loader.py index 7e24681b..fd6ed71b 100644 --- a/tools/Polygraphy/tests/backend/common/test_loader.py +++ b/tools/Polygraphy/tests/backend/common/test_loader.py @@ -26,7 +26,8 @@ from polygraphy.exception import PolygraphyException class TestImporter(object): @pytest.mark.parametrize("loader", [InvokeFromScript, invoke_from_script]) def test_import_from_script(self, loader): - script = dedent(""" + script = dedent( + """ from polygraphy.backend.trt import CreateNetwork from polygraphy import func import tensorrt as trt @@ -36,7 +37,8 @@ class TestImporter(object): inp = network.add_input("input", dtype=trt.float32, shape=(1, 1)) out = network.add_identity(inp).get_output(0) network.mark_output(out) - """) + """ + ) with tempfile.NamedTemporaryFile("w+", suffix=".py") as f: f.write(script) @@ -53,12 +55,13 @@ class TestImporter(object): assert network.num_layers == 1 assert network.get_layer(0).type == trt.LayerType.IDENTITY - def test_import_non_existent(self): - script = dedent(""" + script = dedent( + """ def example(): pass - """) + """ + ) with tempfile.NamedTemporaryFile("w+", suffix=".py") as f: f.write(script) diff --git a/tools/Polygraphy/tests/backend/onnx/test_loader.py b/tools/Polygraphy/tests/backend/onnx/test_loader.py index 07ea7f3f..29029de2 100644 --- a/tools/Polygraphy/tests/backend/onnx/test_loader.py +++ b/tools/Polygraphy/tests/backend/onnx/test_loader.py @@ -19,15 +19,21 @@ import numpy as np import onnx_graphsurgeon as gs import pytest from polygraphy import constants -from polygraphy.backend.onnx import (ConvertToFp16, FoldConstants, - ModifyOutputs, OnnxFromPath, - OnnxFromTfGraph, SaveOnnx, - extract_subgraph, infer_shapes, - onnx_from_path) +from polygraphy.backend.onnx import ( + ConvertToFp16, + FoldConstants, + ModifyOutputs, + OnnxFromPath, + OnnxFromTfGraph, + SaveOnnx, + extract_subgraph, + infer_shapes, + onnx_from_path, +) from polygraphy.common import TensorMetadata from polygraphy.exception import PolygraphyException from polygraphy.logger import G_LOGGER -from tests.helper import check_file_non_empty +from tests.helper import is_file_non_empty from tests.models.meta import ONNX_MODELS, TF_MODELS import onnx @@ -42,13 +48,12 @@ class TestLoggerCallbacks(object): class TestOnnxFileLoader(object): def test_basic(self): loader = OnnxFromPath(ONNX_MODELS["identity"].path) - assert isinstance(loader() , onnx.ModelProto) - + assert isinstance(loader(), onnx.ModelProto) def test_external_data(self): model = ONNX_MODELS["ext_weights"] loader = OnnxFromPath(model.path, model.ext_data) - assert isinstance(loader() , onnx.ModelProto) + assert isinstance(loader(), onnx.ModelProto) class TestExportOnnxFromTf(object): @@ -56,7 +61,6 @@ class TestExportOnnxFromTf(object): loader = OnnxFromTfGraph(TF_MODELS["identity"].loader, optimize=False, fold_constant=False) model = loader() - def test_opset(self): loader = OnnxFromTfGraph(TF_MODELS["identity"].loader, opset=9) model = loader() @@ -72,16 +76,18 @@ class TestModifyOnnx(object): assert len(original_model.graph.output) == 1 or not copy assert len(model.graph.output) == 2 - def test_custom_outputs(self): loader = ModifyOutputs(OnnxFromPath(ONNX_MODELS["identity_identity"].path), outputs=["identity_out_0"]) model = loader() assert len(model.graph.output) == 1 assert model.graph.output[0].name == "identity_out_0" - def test_exclude_outputs_with_layerwise(self): - loader = ModifyOutputs(OnnxFromPath(ONNX_MODELS["identity_identity"].path), outputs=constants.MARK_ALL, exclude_outputs=["identity_out_2"]) + loader = ModifyOutputs( + OnnxFromPath(ONNX_MODELS["identity_identity"].path), + outputs=constants.MARK_ALL, + exclude_outputs=["identity_out_2"], + ) model = loader() assert len(model.graph.output) == 1 assert model.graph.output[0].name == "identity_out_0" @@ -89,20 +95,36 @@ class TestModifyOnnx(object): class TestInferShapes(object): def check_model(self, model): - for output in model.graph.output: - assert output.type.tensor_type.HasField("shape") + # Find all intermediate tensors to check if they have shapes. + tensors = set() + for node in model.graph.node: + tensors.update(node.output) + tensors -= {out.name for out in model.graph.output} + assert len(model.graph.value_info) == len(tensors) + for val in model.graph.value_info: + assert val.type.tensor_type.HasField("shape") def test_model(self): original_model = onnx_from_path(ONNX_MODELS["identity_identity"].path) model = infer_shapes(original_model) self.check_model(model) - def test_path(self): model = infer_shapes(ONNX_MODELS["identity_identity"].path) self.check_model(model) + @pytest.mark.parametrize("set_data_dir", [True, False]) + def test_external_data(self, set_data_dir): + model = ONNX_MODELS["ext_weights_same_dir"] + model = infer_shapes(model.path, external_data_dir=model.ext_data if set_data_dir else None) + self.check_model(model) + + def test_save_to_disk_on_size_threshold(self): + model = onnx_from_path(ONNX_MODELS["const_foldable"].path) + model = infer_shapes(model, save_to_disk_threshold_bytes=0) + self.check_model(model) + class TestConvertToFp16: @pytest.mark.parametrize("copy", [True, False]) @@ -132,16 +154,15 @@ class TestSaveOnnx(object): with tempfile.NamedTemporaryFile() as outpath: loader = SaveOnnx(OnnxFromPath(ONNX_MODELS["identity"].path), path=outpath.name) loader() - check_file_non_empty(outpath.name) - + assert is_file_non_empty(outpath.name) def test_external_data(self): with tempfile.NamedTemporaryFile() as path, tempfile.NamedTemporaryFile() as data: model = OnnxFromPath(ONNX_MODELS["const_foldable"].path) loader = SaveOnnx(model, path.name, external_data_path=data.name, size_threshold=0) loader() - check_file_non_empty(path.name) - check_file_non_empty(data.name) + assert is_file_non_empty(path.name) + assert is_file_non_empty(data.name) @pytest.fixture() @@ -165,7 +186,6 @@ class TestExtractSubgraph(object): assert graph.outputs[0].name == "identity_out_0" assert graph.outputs[0].dtype is not None - def test_extract_onnx_model(self, extract_model): original_model, input_meta, output_meta = extract_model model = extract_subgraph(original_model, input_meta, output_meta) @@ -173,19 +193,16 @@ class TestExtractSubgraph(object): assert original_model.graph.output[0].name == "identity_out_2" self.check_model(model) - def test_extract_onnx_model_no_input_meta(self, extract_model): model, _, output_meta = extract_model model = extract_subgraph(model, output_metadata=output_meta) self.check_model(model) - def test_extract_onnx_model_no_output_meta(self, extract_model): model, input_meta, _ = extract_model model = extract_subgraph(model, input_metadata=input_meta) assert model.graph.output[0].name == "identity_out_2" - def test_extract_onnx_gs_graph(self, extract_model): model, input_meta, output_meta = extract_model graph = gs.import_onnx(model) @@ -199,21 +216,18 @@ class TestExtractSubgraph(object): assert len(graph.outputs) == 1 assert graph.outputs[0].name == "identity_out_0" - def test_extract_passes_no_input_shape(self, extract_model): model, input_meta, output_meta = extract_model input_meta["X"].shape = None model = extract_subgraph(model, input_meta, output_meta) self.check_model(model) - def test_extract_passes_no_input_dtype(self, extract_model): model, input_meta, output_meta = extract_model input_meta["X"].dtype = None model = extract_subgraph(model, input_meta, output_meta) self.check_model(model) - def test_extract_passes_no_output_shape(self, extract_model): model, input_meta, output_meta = extract_model output_meta["identity_out_0"].shape = None diff --git a/tools/Polygraphy/tests/backend/onnx/test_util.py b/tools/Polygraphy/tests/backend/onnx/test_util.py index 12d12009..54534ed6 100644 --- a/tools/Polygraphy/tests/backend/onnx/test_util.py +++ b/tools/Polygraphy/tests/backend/onnx/test_util.py @@ -21,4 +21,4 @@ from tests.models.meta import ONNX_MODELS def test_get_num_nodes(): model = onnx_from_path(ONNX_MODELS["scan"].path) - assert onnx_util.get_num_nodes(model) == 3 # Should count subgraph nodes. + assert onnx_util.get_num_nodes(model) == 3 # Should count subgraph nodes. diff --git a/tools/Polygraphy/tests/backend/onnxrt/test_runner.py b/tools/Polygraphy/tests/backend/onnxrt/test_runner.py index 07560d7f..563ba75d 100644 --- a/tools/Polygraphy/tests/backend/onnxrt/test_runner.py +++ b/tools/Polygraphy/tests/backend/onnxrt/test_runner.py @@ -33,22 +33,18 @@ class TestOnnxrtRunner(object): runner = OnnxrtRunner(None, name=NAME) assert runner.name == NAME - def test_basic(self): model = ONNX_MODELS["identity"] with OnnxrtRunner(SessionFromOnnx(model.loader)) as runner: assert runner.is_active model.check_runner(runner) assert not runner.is_active - assert runner._cached_input_metadata is None - def test_shape_output(self): model = ONNX_MODELS["reshape"] with OnnxrtRunner(SessionFromOnnx(model.loader)) as runner: model.check_runner(runner) - def test_dim_param_preserved(self): model = ONNX_MODELS["dim_param"] with OnnxrtRunner(SessionFromOnnx(model.loader)) as runner: @@ -56,28 +52,28 @@ class TestOnnxrtRunner(object): # In Polygraphy, we only use None to indicate a dynamic input dimension - not strings. assert len(input_meta) == 1 for _, (_, shape) in input_meta.items(): - assert shape == ['dim0', 16, 128] + assert shape == ["dim0", 16, 128] - - @pytest.mark.parametrize("names, err", [ - (["fake-input", "x"], "Extra keys in"), - (["fake-input"], "Some keys are missing"), - ([], "Some keys are missing"), - ]) + @pytest.mark.parametrize( + "names, err", + [ + (["fake-input", "x"], "Extra keys in"), + (["fake-input"], "Some keys are missing"), + ([], "Some keys are missing"), + ], + ) def test_error_on_wrong_name_feed_dict(self, names, err): model = ONNX_MODELS["identity"] with OnnxrtRunner(SessionFromOnnx(model.loader)) as runner: with pytest.raises(PolygraphyException, match=err): runner.infer({name: np.ones(shape=(1, 1, 2, 2), dtype=np.float32) for name in names}) - def test_error_on_wrong_dtype_feed_dict(self): model = ONNX_MODELS["identity"] with OnnxrtRunner(SessionFromOnnx(model.loader)) as runner: with pytest.raises(PolygraphyException, match="unexpected dtype."): runner.infer({"x": np.ones(shape=(1, 1, 2, 2), dtype=np.int32)}) - def test_error_on_wrong_shape_feed_dict(self): model = ONNX_MODELS["identity"] with OnnxrtRunner(SessionFromOnnx(model.loader)) as runner: diff --git a/tools/Polygraphy/tests/backend/tf/test_loader.py b/tools/Polygraphy/tests/backend/tf/test_loader.py index 34ad2ebb..eac16af0 100644 --- a/tools/Polygraphy/tests/backend/tf/test_loader.py +++ b/tools/Polygraphy/tests/backend/tf/test_loader.py @@ -19,10 +19,9 @@ import tempfile import pytest import tensorflow as tf from polygraphy import constants -from polygraphy.backend.tf import (GraphFromFrozen, ModifyGraphOutputs, - SaveGraph, graph_from_frozen) +from polygraphy.backend.tf import GraphFromFrozen, ModifyGraphOutputs, SaveGraph, graph_from_frozen from polygraphy.logger import G_LOGGER -from tests.helper import check_file_non_empty +from tests.helper import is_file_non_empty from tests.models.meta import TF_MODELS @@ -42,7 +41,6 @@ class TestFrozenGraphLoader(object): assert graph assert outputs - def test_load_pb(self): tf_loader = GraphFromFrozen(TF_MODELS["identity"].path) tf_loader() @@ -63,8 +61,7 @@ class TestSaveGraph(object): with tempfile.NamedTemporaryFile() as outpath: tf_loader = SaveGraph(GraphFromFrozen(TF_MODELS["identity"].path), path=outpath.name) tf_loader() - check_file_non_empty(outpath.name) - + assert is_file_non_empty(outpath.name) def test_save_tensorboard(self): with tempfile.TemporaryDirectory() as outdir: diff --git a/tools/Polygraphy/tests/backend/tf/test_runner.py b/tools/Polygraphy/tests/backend/tf/test_runner.py index fb8a4db8..b1175531 100644 --- a/tools/Polygraphy/tests/backend/tf/test_runner.py +++ b/tools/Polygraphy/tests/backend/tf/test_runner.py @@ -19,7 +19,7 @@ import numpy as np import pytest from polygraphy.backend.tf import SessionFromGraph, TfRunner from polygraphy.exception import PolygraphyException -from tests.helper import check_file_non_empty +from tests.helper import is_file_non_empty from tests.models.meta import TF_MODELS @@ -29,15 +29,12 @@ class TestTfRunner(object): runner = TfRunner(None, name=NAME) assert runner.name == NAME - def test_basic(self): model = TF_MODELS["identity"] with TfRunner(SessionFromGraph(model.loader)) as runner: assert runner.is_active model.check_runner(runner) assert not runner.is_active - assert runner._cached_input_metadata is None - @pytest.mark.skip(reason="Non-trivial to set up - requires CUPTI") def test_save_timeline(self): @@ -45,28 +42,28 @@ class TestTfRunner(object): with tempfile.NamedTemporaryFile() as outpath: with TfRunner(SessionFromGraph(model.loader), allow_growth=True, save_timeline=outpath.name) as runner: model.check_runner(runner) - check_file_non_empty(outpath.name) + assert is_file_non_empty(outpath.name) - - @pytest.mark.parametrize("names, err", [ - (["fake-input", "Input:0"], "Extra keys in"), - (["fake-input"], "Some keys are missing"), - ([], "Some keys are missing"), - ]) + @pytest.mark.parametrize( + "names, err", + [ + (["fake-input", "Input:0"], "Extra keys in"), + (["fake-input"], "Some keys are missing"), + ([], "Some keys are missing"), + ], + ) def test_error_on_wrong_name_feed_dict(self, names, err): model = TF_MODELS["identity"] with TfRunner(SessionFromGraph(model.loader)) as runner: with pytest.raises(PolygraphyException, match=err): runner.infer({name: np.ones(shape=(1, 15, 25, 30), dtype=np.float32) for name in names}) - def test_error_on_wrong_dtype_feed_dict(self): model = TF_MODELS["identity"] with TfRunner(SessionFromGraph(model.loader)) as runner: with pytest.raises(PolygraphyException, match="unexpected dtype."): runner.infer({"Input:0": np.ones(shape=(1, 15, 25, 30), dtype=np.int32)}) - def test_error_on_wrong_shape_feed_dict(self): model = TF_MODELS["identity"] with TfRunner(SessionFromGraph(model.loader)) as runner: diff --git a/tools/Polygraphy/tests/backend/trt/test_algorithm_selector.py b/tools/Polygraphy/tests/backend/trt/test_algorithm_selector.py index 23594bd7..1d0052bc 100644 --- a/tools/Polygraphy/tests/backend/trt/test_algorithm_selector.py +++ b/tools/Polygraphy/tests/backend/trt/test_algorithm_selector.py @@ -20,40 +20,90 @@ from collections import namedtuple import pytest import tensorrt as trt from polygraphy import mod -from polygraphy.backend.trt import (Algorithm, TacticRecorder, - TacticReplayData, TacticReplayer) +from polygraphy.backend.trt import Algorithm, TacticRecorder, TacticReplayData, TacticReplayer from polygraphy.exception import PolygraphyException ALGO_EQ_CASES = [ - (Algorithm(6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)]), - Algorithm(6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)]), - True), # Same - (Algorithm(6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)]), - Algorithm(7, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)]), - False), # Different implementation - (Algorithm(6, 2, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)]), - Algorithm(6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)]), - False), # Different tactic - (Algorithm(6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)]), - Algorithm(6, 1, inputs=[(trt.TensorFormat.CHW32, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)]), - False), # Different input format - (Algorithm(6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)]), - Algorithm(6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.int8)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)]), - False), # Different input data type - (Algorithm(6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)]), - Algorithm(6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.CHW32, trt.float32)]), - False), # Different output format - (Algorithm(6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)]), - Algorithm(6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.int8)]), - False), # Different output data type - (Algorithm(6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)] * 2, outputs=[(trt.TensorFormat.LINEAR, trt.float32)]), - Algorithm(6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)]), - False), # Different number of inputs - (Algorithm(6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)] * 2), - Algorithm(6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)]), - False), # Different number of outputs + ( + Algorithm( + 6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)] + ), + Algorithm( + 6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)] + ), + True, + ), # Same + ( + Algorithm( + 6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)] + ), + Algorithm( + 7, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)] + ), + False, + ), # Different implementation + ( + Algorithm( + 6, 2, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)] + ), + Algorithm( + 6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)] + ), + False, + ), # Different tactic + ( + Algorithm( + 6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)] + ), + Algorithm( + 6, 1, inputs=[(trt.TensorFormat.CHW32, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)] + ), + False, + ), # Different input format + ( + Algorithm( + 6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)] + ), + Algorithm(6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.int8)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)]), + False, + ), # Different input data type + ( + Algorithm( + 6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)] + ), + Algorithm( + 6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.CHW32, trt.float32)] + ), + False, + ), # Different output format + ( + Algorithm( + 6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)] + ), + Algorithm(6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.int8)]), + False, + ), # Different output data type + ( + Algorithm( + 6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)] * 2, outputs=[(trt.TensorFormat.LINEAR, trt.float32)] + ), + Algorithm( + 6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)] + ), + False, + ), # Different number of inputs + ( + Algorithm( + 6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)] * 2 + ), + Algorithm( + 6, 1, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], outputs=[(trt.TensorFormat.LINEAR, trt.float32)] + ), + False, + ), # Different number of outputs ] + @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("8.0"), reason="Unsupported for TRT 7.2 and older") class TestAlgorithm(object): @pytest.mark.parametrize("left, right, expected", ALGO_EQ_CASES) @@ -128,37 +178,37 @@ class TestReplayer(object): selected = replayer.select_algorithms(context, [fake_algo(implementation=2), algo, fake_algo(tactic=1)]) assert selected == [1] - def test_new_layer_falls_back(self, replay): _, _, _, replay_data, _ = replay replayer = TacticReplayer(replay_data) - selected = replayer.select_algorithms(fake_context(name="new_layer"), - [fake_algo(2, 1), fake_algo(3, 4), fake_algo(5, 6)]) + selected = replayer.select_algorithms( + fake_context(name="new_layer"), [fake_algo(2, 1), fake_algo(3, 4), fake_algo(5, 6)] + ) assert selected == [0, 1, 2] - def test_missing_algo_fails(self, replay): context, _, _, replay_data, _ = replay replayer = TacticReplayer(replay_data) with pytest.raises(PolygraphyException, match="was not provided by TensorRT as a choice"): assert replayer.select_algorithms(context, [fake_algo(2, 1)]) == [0] - - @pytest.mark.parametrize("algo", [ - fake_algo(2), - fake_algo(tactic=2), - fake_algo(io=[(trt.TensorFormat.CHW32, trt.float32), (trt.TensorFormat.LINEAR, trt.float32)]), - fake_algo(io=[(trt.TensorFormat.LINEAR, trt.int8), (trt.TensorFormat.LINEAR, trt.float32)]), - fake_algo(io=[(trt.TensorFormat.LINEAR, trt.float32), (trt.TensorFormat.CHW32, trt.float32)]), - fake_algo(io=[(trt.TensorFormat.LINEAR, trt.float32), (trt.TensorFormat.LINEAR, trt.int32)]), - ]) + @pytest.mark.parametrize( + "algo", + [ + fake_algo(2), + fake_algo(tactic=2), + fake_algo(io=[(trt.TensorFormat.CHW32, trt.float32), (trt.TensorFormat.LINEAR, trt.float32)]), + fake_algo(io=[(trt.TensorFormat.LINEAR, trt.int8), (trt.TensorFormat.LINEAR, trt.float32)]), + fake_algo(io=[(trt.TensorFormat.LINEAR, trt.float32), (trt.TensorFormat.CHW32, trt.float32)]), + fake_algo(io=[(trt.TensorFormat.LINEAR, trt.float32), (trt.TensorFormat.LINEAR, trt.int32)]), + ], + ) def test_different_algo_fails(self, replay, algo): context, _, _, replay_data, _ = replay replayer = TacticReplayer(replay_data) with pytest.raises(PolygraphyException, match="was not provided by TensorRT as a choice"): assert replayer.select_algorithms(context, [algo]) == [0] - def test_fails_if_wrong_selected(self, replay): context, _, _, replay_data, _ = replay replayer = TacticReplayer(replay_data) diff --git a/tools/Polygraphy/tests/backend/trt/test_calibrator.py b/tools/Polygraphy/tests/backend/trt/test_calibrator.py index 77d52d55..409d3b67 100644 --- a/tools/Polygraphy/tests/backend/trt/test_calibrator.py +++ b/tools/Polygraphy/tests/backend/trt/test_calibrator.py @@ -19,11 +19,15 @@ import numpy as np import pytest import tensorrt as trt from polygraphy import cuda, mod -from polygraphy.backend.trt import (Calibrator, CreateConfig, - engine_from_network, get_trt_logger, - network_from_onnx_bytes) +from polygraphy.backend.trt import ( + Calibrator, + CreateConfig, + engine_from_network, + get_trt_logger, + network_from_onnx_bytes, +) from polygraphy.exception import PolygraphyException -from tests.helper import check_file_non_empty, get_file_size +from tests.helper import is_file_non_empty, get_file_size from tests.models.meta import ONNX_MODELS @@ -51,14 +55,16 @@ class TestCalibrator(object): # Calibrator buffers should be freed after the build assert all([buf.allocated_nbytes == 0 for buf in calibrator.device_buffers.values()]) - - @pytest.mark.parametrize("BaseClass", [ - trt.IInt8Calibrator, - trt.IInt8LegacyCalibrator, - trt.IInt8EntropyCalibrator, - trt.IInt8EntropyCalibrator2, - trt.IInt8MinMaxCalibrator, - ]) + @pytest.mark.parametrize( + "BaseClass", + [ + trt.IInt8Calibrator, + trt.IInt8LegacyCalibrator, + trt.IInt8EntropyCalibrator, + trt.IInt8EntropyCalibrator2, + trt.IInt8MinMaxCalibrator, + ], + ) def test_calibrator_basic(self, identity_builder_network, BaseClass): if mod.version(trt.__version__) < mod.version("7.0") and BaseClass == trt.IInt8LegacyCalibrator: pytest.skip("Bug in TRT 6 causes NaNs with legacy calibrator") @@ -74,7 +80,6 @@ class TestCalibrator(object): assert calibrator.num_batches == NUM_BATCHES self.check_calibrator_cleanup(calibrator) - def test_host_data_copied_to_device(self): with Calibrator(generate_data(1)) as calibrator: [ptr] = calibrator.get_batch(names=["x"]) @@ -83,7 +88,6 @@ class TestCalibrator(object): assert arr.shape == (1, 1, 2, 2) assert np.all(arr == 1) - def test_calibrator_data_and_ordering_correct(self): def generate_multidata(num_batches): for _ in range(num_batches): @@ -101,7 +105,6 @@ class TestCalibrator(object): v = cuda.DeviceView(ptr, shape=(4, 5), dtype=np.float32) assert np.all(v.numpy() == index) - def test_calibrator_generator_data(self, identity_builder_network): builder, network = identity_builder_network NUM_BATCHES = 2 @@ -113,20 +116,17 @@ class TestCalibrator(object): assert calibrator.num_batches == NUM_BATCHES self.check_calibrator_cleanup(calibrator) - # We should be able to mix DeviceView with NumPy arrays. - @pytest.mark.parametrize("mode", ["array", "view", "pointer"]) # We should be able to use DeviceArray in place of DeviceView + @pytest.mark.parametrize( + "mode", ["array", "view", "pointer"] + ) # We should be able to use DeviceArray in place of DeviceView def test_calibrator_device_buffers_multiinput(self, multi_input_builder_network, mode): def generate_dev_data(num_batches): - with cuda.DeviceArray(shape=(1, ), dtype=np.float32) as x: + with cuda.DeviceArray(shape=(1,), dtype=np.float32) as x: for _ in range(num_batches): - x.copy_from(np.ones((1, ), dtype=np.float32)) - xdata = { - "array": x, - "view": cuda.DeviceView(x.ptr, x.shape, x.dtype), - "pointer": x.ptr - }[mode] - yield {"X0": xdata, "Y0": np.zeros((1, ), dtype=np.float32)} + x.copy_from(np.ones((1,), dtype=np.float32)) + xdata = {"array": x, "view": cuda.DeviceView(x.ptr, x.shape, x.dtype), "pointer": x.ptr}[mode] + yield {"X0": xdata, "Y0": np.zeros((1,), dtype=np.float32)} builder, network = multi_input_builder_network NUM_BATCHES = 2 @@ -138,7 +138,6 @@ class TestCalibrator(object): assert calibrator.num_batches == NUM_BATCHES self.check_calibrator_cleanup(calibrator) - # We want the calibrator to inter-op with TRT APIs seamlessly def test_calibrator_outside_polygraphy(self, identity_builder_network): builder, network = identity_builder_network @@ -159,7 +158,6 @@ class TestCalibrator(object): assert engine self.check_calibrator_cleanup(calibrator) - def test_cannot_use_calibrator_without_activation(self): def generate_data(): for item in [np.ones((1, 1, 2, 2), dtype=np.float32)]: @@ -168,7 +166,6 @@ class TestCalibrator(object): calibrator = Calibrator(generate_data()) assert calibrator.get_batch(["x"]) is None - def test_calibrator_with_path_name_cache(self, identity_builder_network): builder, network = identity_builder_network data = [{"x": np.ones((1, 1, 2, 2), dtype=np.float32)}] @@ -177,10 +174,9 @@ class TestCalibrator(object): calibrator = Calibrator(data, cache=cache.name) create_config = CreateConfig(int8=True, calibrator=calibrator) with engine_from_network((builder, network), create_config): - check_file_non_empty(cache.name) + assert is_file_non_empty(cache.name) self.check_calibrator_cleanup(calibrator) - @pytest.mark.parametrize("mode", ["wb+", "rb", "wb"]) def test_calibrator_with_file_object_cache(self, identity_builder_network, mode): builder, network = identity_builder_network @@ -191,10 +187,9 @@ class TestCalibrator(object): create_config = CreateConfig(int8=True, calibrator=calibrator) with engine_from_network((builder, network), create_config): if mode != "rb": - check_file_non_empty(cache.name) + assert is_file_non_empty(cache.name) self.check_calibrator_cleanup(calibrator) - # read_calibration_cache should work even if an explicit cache is not provided # This way, it is possible to calibrate quickly when calibrating multiple times. def test_calibrator_caches_without_explicit_cache(self, identity_builder_network): @@ -211,7 +206,6 @@ class TestCalibrator(object): assert calibrator.read_calibration_cache() self.check_calibrator_cleanup(calibrator) - def test_calibrator_rechecks_cache_on_reset(self, identity_builder_network): builder, network = identity_builder_network data = [{"x": np.ones((1, 1, 2, 2), dtype=np.float32)}] @@ -230,11 +224,13 @@ class TestCalibrator(object): self.check_calibrator_cleanup(calibrator) - - @pytest.mark.parametrize("names", [ - (["fake-input", "x"]), - (["fake-input"]), - ]) + @pytest.mark.parametrize( + "names", + [ + (["fake-input", "x"]), + (["fake-input"]), + ], + ) def test_calibrator_invalid_input_fails(self, identity_builder_network, names): builder, network = identity_builder_network diff --git a/tools/Polygraphy/tests/backend/trt/test_loader.py b/tools/Polygraphy/tests/backend/trt/test_loader.py index 954b607d..d4456a0d 100644 --- a/tools/Polygraphy/tests/backend/trt/test_loader.py +++ b/tools/Polygraphy/tests/backend/trt/test_loader.py @@ -19,23 +19,33 @@ import tempfile import pytest import tensorrt as trt from polygraphy import constants, mod -from polygraphy.backend.trt import (Calibrator, CreateConfig, - EngineBytesFromNetwork, EngineFromBytes, - EngineFromNetwork, LoadPlugins, - ModifyNetworkOutputs, NetworkFromOnnxBytes, - Profile, SaveEngine, bytes_from_engine, - engine_from_network, - modify_network_outputs, - network_from_onnx_bytes, - network_from_onnx_path) +from polygraphy.backend.trt import ( + Calibrator, + CreateConfig, + EngineBytesFromNetwork, + EngineFromBytes, + EngineFromNetwork, + LoadPlugins, + ModifyNetworkOutputs, + NetworkFromOnnxBytes, + Profile, + SaveEngine, + bytes_from_engine, + engine_from_network, + modify_network_outputs, + network_from_onnx_bytes, + network_from_onnx_path, + onnx_like_from_network, +) from polygraphy.comparator import DataLoader -from tests.helper import check_file_non_empty, get_file_size +from tests.helper import is_file_non_empty, get_file_size from tests.models.meta import ONNX_MODELS ## ## Fixtures ## + @pytest.fixture(scope="session") def identity_engine(): network_loader = NetworkFromOnnxBytes(ONNX_MODELS["identity"].loader) @@ -88,6 +98,7 @@ def modifiable_reshape_network(): ## Tests ## + class TestLoadPlugins(object): def test_can_load_libnvinfer_plugins(self): def get_plugin_names(): @@ -108,7 +119,6 @@ class TestSerializedEngineLoader(object): with loader() as engine: assert isinstance(engine, trt.ICudaEngine) - def test_serialized_engine_loader_from_buffer(self, identity_engine): with identity_engine.serialize() as buffer: loader = EngineFromBytes(buffer) @@ -123,7 +133,6 @@ class TestOnnxNetworkLoader(object): assert not network.has_implicit_batch_dimension assert not network.has_explicit_precision - def test_loader_explicit_precision(self): builder, network, parser = network_from_onnx_bytes(ONNX_MODELS["identity"].loader, explicit_precision=True) with builder, network, parser: @@ -140,7 +149,6 @@ class TestNetworkFromOnnxPath(object): assert not network.has_implicit_batch_dimension assert not network.has_explicit_precision - def test_loader_explicit_precision(self): builder, network, parser = network_from_onnx_path(ONNX_MODELS["identity"].path, explicit_precision=True) with builder, network, parser: @@ -158,33 +166,35 @@ class TestModifyNetwork(object): for index in range(layer.num_outputs): assert layer.get_output(index).is_network_output - def test_mark_custom_outputs(self, modifiable_network): builder, network, parser = modify_network_outputs(modifiable_network, outputs=["identity_out_0"]) with builder, network, parser: assert network.num_outputs == 1 assert network.get_output(0).name == "identity_out_0" - def test_exclude_outputs_with_mark_layerwise(self, modifiable_network): - builder, network, parser = modify_network_outputs(modifiable_network, outputs=constants.MARK_ALL, exclude_outputs=["identity_out_2"]) + builder, network, parser = modify_network_outputs( + modifiable_network, outputs=constants.MARK_ALL, exclude_outputs=["identity_out_2"] + ) with builder, network, parser: assert network.num_outputs == 1 assert network.get_output(0).name == "identity_out_0" - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") def test_mark_shape_outputs(self, modifiable_reshape_network): - builder, network, parser = modify_network_outputs(modifiable_reshape_network, outputs=["output", "reduce_prod_out_gs_2"]) + builder, network, parser = modify_network_outputs( + modifiable_reshape_network, outputs=["output", "reduce_prod_out_gs_2"] + ) with builder, network, parser: assert network.num_outputs == 2 assert network.get_output(0).name == "reduce_prod_out_gs_2" assert network.get_output(0).is_shape_tensor - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") def test_unmark_shape_outputs(self, modifiable_reshape_network): - builder, network, parser = modify_network_outputs(modifiable_reshape_network, outputs=constants.MARK_ALL, exclude_outputs=["reduce_prod_out_gs_2"]) + builder, network, parser = modify_network_outputs( + modifiable_reshape_network, outputs=constants.MARK_ALL, exclude_outputs=["reduce_prod_out_gs_2"] + ) with builder, network, parser: assert network.num_outputs == 1 @@ -211,14 +221,12 @@ class TestConfigLoader(object): else: assert config.get_tactic_sources() == 7 - def test_workspace_size(self, identity_builder_network): builder, network = identity_builder_network loader = CreateConfig(max_workspace_size=0) with loader(builder, network) as config: assert config.max_workspace_size == 0 - @pytest.mark.parametrize("flag", [True, False]) def test_strict_types(self, identity_builder_network, flag): builder, network = identity_builder_network @@ -226,6 +234,13 @@ class TestConfigLoader(object): with loader(builder, network) as config: assert config.get_flag(trt.BuilderFlag.STRICT_TYPES) == flag + @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("8.0.0.0"), reason="API was added in TRT 8.0") + @pytest.mark.parametrize("flag", [True, False]) + def test_restricted(self, identity_builder_network, flag): + builder, network = identity_builder_network + loader = CreateConfig(restricted=flag) + with loader(builder, network) as config: + assert config.get_flag(trt.BuilderFlag.SAFETY_SCOPE) == flag @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.1.0.0"), reason="API was added in TRT 7.1") @pytest.mark.parametrize("flag", [True, False]) @@ -235,7 +250,6 @@ class TestConfigLoader(object): with loader(builder, network) as config: assert config.get_flag(trt.BuilderFlag.TF32) == flag - @pytest.mark.parametrize("flag", [True, False]) def test_fp16(self, identity_builder_network, flag): builder, network = identity_builder_network @@ -243,7 +257,6 @@ class TestConfigLoader(object): with loader(builder, network) as config: assert config.get_flag(trt.BuilderFlag.FP16) == flag - @pytest.mark.parametrize("flag", [True, False]) def test_int8(self, identity_builder_network, flag): builder, network = identity_builder_network @@ -251,8 +264,9 @@ class TestConfigLoader(object): with loader(builder, network) as config: assert config.get_flag(trt.BuilderFlag.INT8) == flag - - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("8.0"), reason="API was not available in 7.2 and older") + @pytest.mark.skipif( + mod.version(trt.__version__) < mod.version("8.0"), reason="API was not available in 7.2 and older" + ) @pytest.mark.parametrize("flag", [True, False]) def test_sparse_weights(self, identity_builder_network, flag): builder, network = identity_builder_network @@ -260,19 +274,18 @@ class TestConfigLoader(object): with loader(builder, network) as config: assert config.get_flag(trt.BuilderFlag.SPARSE_WEIGHTS) == flag - with contextlib.suppress(AttributeError): if mod.version(trt.__version__) < mod.version("8.0"): - TACTIC_SOURCES_CASES = [ - (None, 3), # By default, all sources are enabled. + TACTIC_SOURCES_CASES = [ + (None, 3), # By default, all sources are enabled. ([], 0), ([trt.TacticSource.CUBLAS], 1), ([trt.TacticSource.CUBLAS_LT], 2), ([trt.TacticSource.CUBLAS, trt.TacticSource.CUBLAS_LT], 3), ] else: - TACTIC_SOURCES_CASES = [ - (None, 7), # By default, all sources are enabled. + TACTIC_SOURCES_CASES = [ + (None, 7), # By default, all sources are enabled. ([], 0), ([trt.TacticSource.CUBLAS], 1), ([trt.TacticSource.CUBLAS_LT], 2), @@ -290,7 +303,6 @@ class TestConfigLoader(object): with loader(builder, network) as config: assert config.get_tactic_sources() == expected - def test_calibrator_metadata_set(self, identity_builder_network): builder, network = identity_builder_network calibrator = Calibrator(DataLoader()) @@ -299,7 +311,6 @@ class TestConfigLoader(object): assert config.int8_calibrator assert "x" in calibrator.data_loader.input_metadata - def test_multiple_profiles(self, identity_builder_network): builder, network = identity_builder_network profiles = [ @@ -310,7 +321,6 @@ class TestConfigLoader(object): with loader(builder, network) as config: assert config.num_optimization_profiles == 2 - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("8.0"), reason="Unsupported for TRT 7.2 and older") @pytest.mark.parametrize("path_mode", [True, False], ids=["path", "file-like"]) def test_timing_cache(self, identity_builder_network, path_mode): @@ -320,7 +330,6 @@ class TestConfigLoader(object): with loader(builder, network) as config: assert config.get_timing_cache() - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("8.0"), reason="Unsupported for TRT 7.2 and older") def test_empty_timing_cache_when_default(self, identity_builder_network): builder, network = identity_builder_network @@ -348,20 +357,17 @@ class TestEngineFromNetwork(object): loader = EngineFromNetwork(identity_network) assert loader.timing_cache_path is None - def test_can_build_with_parser_owning(self, identity_network): loader = EngineFromNetwork(identity_network) with loader(): pass - def test_can_build_without_parser_non_owning(self, identity_builder_network): builder, network = identity_builder_network loader = EngineFromNetwork((builder, network)) with loader(): pass - def test_can_build_with_calibrator(self, identity_builder_network): builder, network = identity_builder_network calibrator = Calibrator(DataLoader()) @@ -372,7 +378,6 @@ class TestEngineFromNetwork(object): # Calibrator buffers should be freed after the build assert all([buf.allocated_nbytes == 0 for buf in calibrator.device_buffers.values()]) - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("8.0"), reason="Unsupported for TRT 7.2 and older") @pytest.mark.parametrize("path_mode", [True, False], ids=["path", "file-like"]) def test_timing_cache_generate_and_append(self, path_mode): @@ -385,8 +390,11 @@ class TestEngineFromNetwork(object): # In non-path_mode, use the file-like object directly. # Must load the cache with CreateConfig so that new data is appended # instead of overwriting the previous cache. - loader = EngineFromNetwork(network_loader, CreateConfig(load_timing_cache=cache.name), - save_timing_cache=cache.name if path_mode else cache) + loader = EngineFromNetwork( + network_loader, + CreateConfig(load_timing_cache=cache.name), + save_timing_cache=cache.name if path_mode else cache, + ) with loader(): pass if not path_mode: @@ -424,4 +432,13 @@ class TestSaveEngine(object): with tempfile.NamedTemporaryFile() as outpath: engine_loader = SaveEngine(EngineFromNetwork(identity_network), path=outpath.name) with engine_loader(): - check_file_non_empty(outpath.name) + assert is_file_non_empty(outpath.name) + + +class TestOnnxLikeFromNetwork(object): + @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.2"), reason="Unsupported for TRT 7.1 and older") + @pytest.mark.parametrize( + "model_name", ["identity", "empty_tensor_expand", "const_foldable", "and", "scan", "dim_param", "tensor_attr"] + ) + def test_onnx_like_from_network(self, model_name): + assert onnx_like_from_network(NetworkFromOnnxBytes(ONNX_MODELS[model_name].loader)) diff --git a/tools/Polygraphy/tests/backend/trt/test_profile.py b/tools/Polygraphy/tests/backend/trt/test_profile.py index c99a3e23..94898fd9 100644 --- a/tools/Polygraphy/tests/backend/trt/test_profile.py +++ b/tools/Polygraphy/tests/backend/trt/test_profile.py @@ -38,7 +38,6 @@ class TestProfile(object): assert shape_tuple.opt == opt assert shape_tuple.max == max - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") def test_fill_defaults_does_not_overwrite(self, dynamic_identity_network): _, network, _ = dynamic_identity_network @@ -49,7 +48,6 @@ class TestProfile(object): assert profile["X"].opt == (1, 1, 2, 2) assert profile["X"].max == (1, 1, 3, 3) - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") def test_to_trt(self, dynamic_identity_network): builder, network, _ = dynamic_identity_network diff --git a/tools/Polygraphy/tests/backend/trt/test_runner.py b/tools/Polygraphy/tests/backend/trt/test_runner.py index 35cb7cd8..8dde85f7 100644 --- a/tools/Polygraphy/tests/backend/trt/test_runner.py +++ b/tools/Polygraphy/tests/backend/trt/test_runner.py @@ -19,9 +19,14 @@ import numpy as np import pytest import tensorrt as trt from polygraphy import cuda, mod -from polygraphy.backend.trt import (CreateConfig, EngineFromNetwork, - NetworkFromOnnxBytes, Profile, TrtRunner, - engine_from_network) +from polygraphy.backend.trt import ( + CreateConfig, + EngineFromNetwork, + NetworkFromOnnxBytes, + Profile, + TrtRunner, + engine_from_network, +) from polygraphy.exception import PolygraphyException from polygraphy.logger import G_LOGGER from tests.models.meta import ONNX_MODELS @@ -39,7 +44,6 @@ class TestTrtRunner(object): runner = TrtRunner(None, name=NAME) assert runner.name == NAME - def test_basic(self): model = ONNX_MODELS["identity"] network_loader = NetworkFromOnnxBytes(model.loader) @@ -49,8 +53,6 @@ class TestTrtRunner(object): assert runner.owns_context model.check_runner(runner) assert not runner.is_active - assert runner._cached_input_metadata is None - def test_context(self): model = ONNX_MODELS["identity"] @@ -60,7 +62,6 @@ class TestTrtRunner(object): assert not runner.owns_engine assert runner.owns_context - def test_device_buffer_order_matches_bindings(self): model = ONNX_MODELS["reducable"] engine = engine_from_network(NetworkFromOnnxBytes(model.loader)) @@ -69,7 +70,6 @@ class TestTrtRunner(object): for binding, dev_buf_name in zip(engine, dev_buf_order): assert binding == dev_buf_name - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") def test_shape_output(self): model = ONNX_MODELS["reshape"] @@ -77,38 +77,38 @@ class TestTrtRunner(object): with engine, TrtRunner(engine.create_execution_context) as runner: model.check_runner(runner) - def test_multithreaded_runners_from_engine(self): model = ONNX_MODELS["identity"] engine = engine_from_network(NetworkFromOnnxBytes(model.loader)) with engine, TrtRunner(engine) as runner0, TrtRunner(engine) as runner1: - t1 = threading.Thread(target=model.check_runner, args=(runner0, )) - t2 = threading.Thread(target=model.check_runner, args=(runner1, )) + t1 = threading.Thread(target=model.check_runner, args=(runner0,)) + t2 = threading.Thread(target=model.check_runner, args=(runner1,)) t1.start() t2.start() t1.join() t2.join() - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") + @pytest.mark.skipif(mod.version(trt.__version__)[0:2] == mod.version("7.2"), reason="Bugged in TRT 7.2") def test_multiple_profiles(self): model = ONNX_MODELS["dynamic_identity"] - profile0_shapes = [(1, 2, 1, 1), (1, 2, 2, 2), (1, 2, 4, 4)] - profile1_shapes = [(1, 2, 4, 4), (1, 2, 8, 8), (1, 2, 16, 16)] + profile0_shapes = [(1, 2, 1, 1), (1, 2, 1, 1), (1, 2, 1, 1)] # Use min==opt==max to fix shapes in the engine. + profile1_shapes = [(1, 2, 1, 1), (1, 2, 2, 2), (1, 2, 4, 4)] + profile2_shapes = [(1, 2, 4, 4), (1, 2, 8, 8), (1, 2, 16, 16)] network_loader = NetworkFromOnnxBytes(model.loader) profiles = [ Profile().add("X", *profile0_shapes), Profile().add("X", *profile1_shapes), + Profile().add("X", *profile2_shapes), ] config_loader = CreateConfig(profiles=profiles) with TrtRunner(EngineFromNetwork(network_loader, config_loader)) as runner: - for index, shapes in enumerate([profile0_shapes, profile1_shapes]): + for index, shapes in enumerate([profile0_shapes, profile1_shapes, profile2_shapes]): runner.set_profile(index) for shape in shapes: model.check_runner(runner, {"X": shape}) - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") def test_empty_tensor_with_dynamic_input_shape_tensor(self): model = ONNX_MODELS["empty_tensor_expand"] @@ -121,13 +121,15 @@ class TestTrtRunner(object): for shape in shapes: model.check_runner(runner, {"new_shape": shape}) - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Test not compatible with TRT 6") - @pytest.mark.parametrize("names, err", [ - (["fake-input", "x"], "Extra keys in"), - (["fake-input"], "Some keys are missing"), - ([], "Some keys are missing"), - ]) + @pytest.mark.parametrize( + "names, err", + [ + (["fake-input", "x"], "Extra keys in"), + (["fake-input"], "Some keys are missing"), + ([], "Some keys are missing"), + ], + ) def test_error_on_wrong_name_feed_dict(self, names, err): model = ONNX_MODELS["identity"] network_loader = NetworkFromOnnxBytes(model.loader) @@ -135,7 +137,6 @@ class TestTrtRunner(object): with pytest.raises(PolygraphyException, match=err): runner.infer({name: np.ones(shape=(1, 1, 2, 2), dtype=np.float32) for name in names}) - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Test not compatible with TRT 6") def test_error_on_wrong_dtype_feed_dict(self): model = ONNX_MODELS["identity"] @@ -144,7 +145,6 @@ class TestTrtRunner(object): with pytest.raises(PolygraphyException, match="unexpected dtype."): runner.infer({"x": np.ones(shape=(1, 1, 2, 2), dtype=np.int32)}) - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Test not compatible with TRT 6") def test_error_on_wrong_shape_feed_dict(self): model = ONNX_MODELS["identity"] @@ -153,18 +153,21 @@ class TestTrtRunner(object): with pytest.raises(PolygraphyException, match="incompatible shape."): runner.infer({"x": np.ones(shape=(1, 1, 3, 2), dtype=np.float32)}) - - @pytest.mark.parametrize("use_view", [True, False]) # We should be able to use DeviceArray in place of DeviceView + @pytest.mark.parametrize("use_view", [True, False]) # We should be able to use DeviceArray in place of DeviceView def test_device_views(self, use_view): model = ONNX_MODELS["reducable"] network_loader = NetworkFromOnnxBytes(model.loader) - with TrtRunner(EngineFromNetwork(network_loader)) as runner, cuda.DeviceArray((1, ), dtype=np.float32) as x: - x.copy_from(np.ones((1, ), dtype=np.float32)) - outputs = runner.infer({"X0": cuda.DeviceView(x.ptr, x.shape, x.dtype) if use_view else x, "Y0": np.ones((1, ), dtype=np.float32)}) + with TrtRunner(EngineFromNetwork(network_loader)) as runner, cuda.DeviceArray((1,), dtype=np.float32) as x: + x.copy_from(np.ones((1,), dtype=np.float32)) + outputs = runner.infer( + { + "X0": cuda.DeviceView(x.ptr, x.shape, x.dtype) if use_view else x, + "Y0": np.ones((1,), dtype=np.float32), + } + ) assert outputs["identity_out_6"][0] == 2 assert outputs["identity_out_8"][0] == 2 - def test_subsequent_infers_with_different_input_types(self): model = ONNX_MODELS["identity"] network_loader = NetworkFromOnnxBytes(model.loader) @@ -178,9 +181,8 @@ class TestTrtRunner(object): check(runner.infer({"x": cuda.DeviceArray().copy_from(inp)})) check(runner.infer({"x": inp})) - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") - @pytest.mark.parametrize("use_view", [True, False]) # We should be able to use DeviceArray in place of DeviceView + @pytest.mark.parametrize("use_view", [True, False]) # We should be able to use DeviceArray in place of DeviceView def test_device_view_dynamic_shapes(self, use_view): model = ONNX_MODELS["dynamic_identity"] profiles = [ @@ -194,10 +196,11 @@ class TestTrtRunner(object): assert np.all(outputs["Y"] == inp) assert outputs["Y"].shape == (1, 2, 3, 3) - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("8.0"), reason="Unsupported before TRT 8") def test_cannot_use_device_view_shape_tensor(self): model = ONNX_MODELS["empty_tensor_expand"] - with TrtRunner(EngineFromNetwork(NetworkFromOnnxBytes(model.loader))) as runner, cuda.DeviceArray(shape=(5, ), dtype=np.int32) as arr: + with TrtRunner(EngineFromNetwork(NetworkFromOnnxBytes(model.loader))) as runner, cuda.DeviceArray( + shape=(5,), dtype=np.int32 + ) as arr: with pytest.raises(PolygraphyException, match="it must reside in host memory"): runner.infer({"data": np.ones((2, 0, 3, 0), dtype=np.float32), "new_shape": arr}) diff --git a/tools/Polygraphy/tests/common/test_interface.py b/tools/Polygraphy/tests/common/test_interface.py index 055b13ca..f5278c95 100644 --- a/tools/Polygraphy/tests/common/test_interface.py +++ b/tools/Polygraphy/tests/common/test_interface.py @@ -33,12 +33,10 @@ class TestTypedDict(object): with pytest.raises(PolygraphyException, match="Unsupported value type"): int_to_float[0] = "hi" - def test_wrong_type_set_item_key(self, int_to_float): with pytest.raises(PolygraphyException, match="Unsupported key type"): int_to_float["hi"] = 1.0 - def test_wrong_type_update(self, int_to_float): with pytest.raises(PolygraphyException, match="Unsupported key type"): int_to_float.update({"hi": 1.0}) @@ -57,17 +55,14 @@ class TestTypedList(object): with pytest.raises(PolygraphyException, match="Unsupported element type"): ints.append(1.0) - def test_wrong_type_extend(self, ints): with pytest.raises(PolygraphyException, match="Unsupported element type"): ints.extend([0, 1, 2, 3, "surprise"]) - def test_wrong_type_iadd(self, ints): with pytest.raises(PolygraphyException, match="Unsupported element type"): ints += [0, 1.0] - def test_wrong_type_setitem(self, ints): ints.append(0) with pytest.raises(PolygraphyException, match="Unsupported element type"): diff --git a/tools/Polygraphy/tests/common/test_struct.py b/tools/Polygraphy/tests/common/test_struct.py index fcbd3db0..df345452 100644 --- a/tools/Polygraphy/tests/common/test_struct.py +++ b/tools/Polygraphy/tests/common/test_struct.py @@ -22,17 +22,14 @@ class TestTensorMetadata(object): meta = TensorMetadata().add("X", dtype=np.float32, shape=(64, 64)) assert str(meta) == "{X [dtype=float32, shape=(64, 64)]}" - def test_str_no_dtype(self): meta = TensorMetadata().add("X", dtype=None, shape=(64, 64)) assert str(meta) == "{X [shape=(64, 64)]}" - def test_str_no_shape(self): meta = TensorMetadata().add("X", dtype=np.float32, shape=None) assert str(meta) == "{X [dtype=float32]}" - def test_str_no_meta(self): meta = TensorMetadata().add("X", dtype=None, shape=None) assert str(meta) == "{X}" diff --git a/tools/Polygraphy/tests/comparator/test_comparator.py b/tools/Polygraphy/tests/comparator/test_comparator.py index c37dff41..82b5e022 100644 --- a/tools/Polygraphy/tests/comparator/test_comparator.py +++ b/tools/Polygraphy/tests/comparator/test_comparator.py @@ -21,12 +21,9 @@ import tensorrt as trt from polygraphy.backend.onnx import BytesFromOnnx, OnnxFromTfGraph from polygraphy.backend.onnxrt import OnnxrtRunner, SessionFromOnnx from polygraphy.backend.tf import SessionFromGraph, TfRunner -from polygraphy.backend.trt import (EngineFromNetwork, NetworkFromOnnxBytes, - TrtRunner) +from polygraphy.backend.trt import EngineFromNetwork, NetworkFromOnnxBytes, TrtRunner from polygraphy.exception import PolygraphyException -from polygraphy.comparator import (Comparator, CompareFunc, DataLoader, - IterationResult, PostprocessFunc, - RunResults) +from polygraphy.comparator import Comparator, CompareFunc, DataLoader, IterationResult, PostprocessFunc, RunResults from polygraphy import mod from tests.models.meta import ONNX_MODELS, TF_MODELS @@ -38,7 +35,6 @@ class TestComparator(object): run_results = Comparator.run([runner], warm_up=2) assert len(run_results[runner.name]) == 1 - def test_list_as_data_loader(self): onnx_loader = ONNX_MODELS["identity"].loader runner = OnnxrtRunner(SessionFromOnnx(onnx_loader), name="onnx_runner") @@ -48,8 +44,7 @@ class TestComparator(object): iter_results = run_results["onnx_runner"] assert len(iter_results) == 2 for actual, expected in zip(iter_results, data): - assert np.all(actual['y'] == expected['x']) - + assert np.all(actual["y"] == expected["x"]) def test_generator_as_data_loader(self): onnx_loader = ONNX_MODELS["identity"].loader @@ -63,8 +58,7 @@ class TestComparator(object): iter_results = run_results["onnx_runner"] assert len(iter_results) == 2 for actual, expected in zip(iter_results, data()): - assert np.all(actual['y'] == expected['x']) - + assert np.all(actual["y"] == expected["x"]) def test_multiple_runners(self): load_tf = TF_MODELS["identity"].loader @@ -82,8 +76,7 @@ class TestComparator(object): run_results = Comparator.run(runners) compare_func = CompareFunc.basic_compare_func(check_shapes=mod.version(trt.__version__) >= mod.version("7.0")) assert bool(Comparator.compare_accuracy(run_results, compare_func=compare_func)) - assert len(list(run_results.values())[0]) == 1 # Default number of iterations - + assert len(list(run_results.values())[0]) == 1 # Default number of iterations def test_postprocess(self): onnx_loader = ONNX_MODELS["identity"].loader @@ -95,7 +88,6 @@ class TestComparator(object): for _, output in result.items(): assert output.shape == (1, 1, 2, 1) - def test_errors_do_not_hang(self): # Should error because interface is not implemented correctly. class FakeRunner(object): @@ -106,7 +98,6 @@ class TestComparator(object): with pytest.raises(PolygraphyException): Comparator.run(runners, use_subprocess=True, subprocess_polling_interval=1) - def test_segfault_does_not_hang(self): def raise_called_process_error(): class FakeSegfault(sp.CalledProcessError): @@ -118,7 +109,6 @@ class TestComparator(object): with pytest.raises(PolygraphyException): Comparator.run(runners, use_subprocess=True, subprocess_polling_interval=1) - def test_multirun_outputs_are_different(self): onnx_loader = ONNX_MODELS["identity"].loader runner = TrtRunner(EngineFromNetwork(NetworkFromOnnxBytes(onnx_loader))) @@ -129,19 +119,16 @@ class TestComparator(object): for name in iteration0.keys(): assert np.any(iteration0[name] != iteration1[name]) - def test_validate_nan(self): run_results = RunResults() run_results["fake-runner"] = [IterationResult(outputs={"x": np.array(np.nan)})] assert not Comparator.validate(run_results) - def test_validate_inf(self): run_results = RunResults() run_results["fake-runner"] = [IterationResult(outputs={"x": np.array(np.inf)})] assert not Comparator.validate(run_results, check_inf=True) - def test_dim_param_trt_onnxrt(self): load_onnx_bytes = ONNX_MODELS["dim_param"].loader build_onnxrt_session = SessionFromOnnx(load_onnx_bytes) @@ -155,4 +142,4 @@ class TestComparator(object): run_results = Comparator.run(runners) compare_func = CompareFunc.basic_compare_func(check_shapes=mod.version(trt.__version__) >= mod.version("7.0")) assert bool(Comparator.compare_accuracy(run_results, compare_func=compare_func)) - assert len(list(run_results.values())[0]) == 1 # Default number of iterations + assert len(list(run_results.values())[0]) == 1 # Default number of iterations diff --git a/tools/Polygraphy/tests/comparator/test_compare.py b/tools/Polygraphy/tests/comparator/test_compare.py index c21b8acf..c3d3f789 100644 --- a/tools/Polygraphy/tests/comparator/test_compare.py +++ b/tools/Polygraphy/tests/comparator/test_compare.py @@ -31,7 +31,6 @@ class TestBasicCompareFunc(object): assert not acc["output"] - @pytest.mark.parametrize("mode", ["abs", "rel"]) def test_per_output_tol(self, mode): OUT0_NAME = "output0" @@ -62,7 +61,6 @@ class TestBasicCompareFunc(object): assert acc[OUT0_NAME] assert acc[OUT1_NAME] - @pytest.mark.parametrize("mode", ["abs", "rel"]) def test_per_output_tol_fallback(self, mode): OUT0_NAME = "output0" @@ -90,7 +88,6 @@ class TestBasicCompareFunc(object): assert not acc[OUT0_NAME] assert acc[OUT1_NAME] - @pytest.mark.parametrize("mode", ["abs", "rel"]) def test_default_tol_in_map(self, mode): # "" can be used to indicate a global tolerance @@ -112,13 +109,15 @@ class TestBasicCompareFunc(object): acc = compare_func(iter_result0, iter_result1) assert acc[OUT0_NAME] - - @pytest.mark.parametrize("shape", [ - tuple(), - (0, 2, 1, 2), - (1, ), - (2, 2, 2, 2), - ]) + @pytest.mark.parametrize( + "shape", + [ + tuple(), + (0, 2, 1, 2), + (1,), + (2, 2, 2, 2), + ], + ) def test_non_matching_outputs(self, shape): iter_result0 = IterationResult(outputs={"output": np.zeros(shape, dtype=np.float32)}) iter_result1 = IterationResult(outputs={"output": np.ones(shape, dtype=np.float32)}) @@ -130,15 +129,17 @@ class TestBasicCompareFunc(object): assert util.is_empty_shape(shape) or not acc["output"] - @pytest.mark.parametrize("check_error_stat", ["max", "median", "mean", "elemwise"]) - @pytest.mark.parametrize("func", [ - np.zeros, - np.ones, - ]) + @pytest.mark.parametrize( + "func", + [ + np.zeros, + np.ones, + ], + ) def test_check_error_stat(self, func, check_error_stat): - iter_result0 = IterationResult(outputs={"output": func((100, ), dtype=np.float32)}) - iter_result1 = IterationResult(outputs={"output": func((100, ), dtype=np.float32)}) + iter_result0 = IterationResult(outputs={"output": func((100,), dtype=np.float32)}) + iter_result1 = IterationResult(outputs={"output": func((100,), dtype=np.float32)}) iter_result0["output"][0] += 100 @@ -151,7 +152,6 @@ class TestBasicCompareFunc(object): else: assert compare_func(iter_result0, iter_result1)["output"] - @pytest.mark.parametrize("check_error_stat", ["max", "median", "mean", "elemwise"]) def test_atol_rtol_either_pass(self, check_error_stat): # If either rtol/atol is sufficient, the compare_func should pass @@ -163,7 +163,6 @@ class TestBasicCompareFunc(object): assert CompareFunc.basic_compare_func(check_error_stat=check_error_stat, rtol=0.25)(res0, res1)["output"] assert CompareFunc.basic_compare_func(check_error_stat=check_error_stat, atol=0.5)(res0, res1)["output"] - def test_atol_rtol_combined_pass(self): # We should also be able to mix them - i.e. rtol might enough for some, atol for others. # If they cover the entire output range, it should pass. @@ -177,23 +176,29 @@ class TestBasicCompareFunc(object): assert CompareFunc.basic_compare_func(atol=0.3, rtol=0.25)(res0, res1)["output"] - - @pytest.mark.parametrize("check_error_stat", [ - {"output0": "mean", "output1": "max"}, - {"": "mean", "output1": "elemwise"}, - {"output0": "mean"}, - {"": "mean"}, - ]) + @pytest.mark.parametrize( + "check_error_stat", + [ + {"output0": "mean", "output1": "max"}, + {"": "mean", "output1": "elemwise"}, + {"output0": "mean"}, + {"": "mean"}, + ], + ) def test_per_output_error_stat(self, check_error_stat): # output0 will only pass when using check_error_stat=mean - res0 = IterationResult(outputs={ - "output0": np.array([0, 1, 2, 3], dtype=np.float32), - "output1": np.array([0, 1, 2, 3], dtype=np.float32), - }) - res1 = IterationResult(outputs={ - "output0": np.array((0.15, 1.25, 2.5, 3.75), dtype=np.float32), - "output1": np.array((0, 1, 2, 3), dtype=np.float32), - }) + res0 = IterationResult( + outputs={ + "output0": np.array([0, 1, 2, 3], dtype=np.float32), + "output1": np.array([0, 1, 2, 3], dtype=np.float32), + } + ) + res1 = IterationResult( + outputs={ + "output0": np.array((0.15, 1.25, 2.5, 3.75), dtype=np.float32), + "output1": np.array((0, 1, 2, 3), dtype=np.float32), + } + ) atol = 0.4125 assert not CompareFunc.basic_compare_func(atol=atol)(res0, res1)["output0"] @@ -201,7 +206,6 @@ class TestBasicCompareFunc(object): assert CompareFunc.basic_compare_func(check_error_stat=check_error_stat, atol=atol)(res0, res1)["output0"] assert CompareFunc.basic_compare_func(check_error_stat=check_error_stat, atol=atol)(res0, res1)["output1"] - def test_invalid_error_stat(self): res0 = IterationResult(outputs={"output": np.array([0, 1, 2, 3], dtype=np.float32)}) res1 = IterationResult(outputs={"output": np.array((0.15, 1.25, 2.5, 3.75), dtype=np.float32)}) diff --git a/tools/Polygraphy/tests/comparator/test_data_loader.py b/tools/Polygraphy/tests/comparator/test_data_loader.py index 7aac2e6a..46fdf37e 100644 --- a/tools/Polygraphy/tests/comparator/test_data_loader.py +++ b/tools/Polygraphy/tests/comparator/test_data_loader.py @@ -24,9 +24,8 @@ import pytest def meta(dtype): - return TensorMetadata().add( - "X", dtype=dtype, shape=(4, 4)).add( - "Y", dtype=dtype, shape=(5, 5)) + return TensorMetadata().add("X", dtype=dtype, shape=(4, 4)).add("Y", dtype=dtype, shape=(5, 5)) + class TestDataLoader(object): @pytest.mark.parametrize("dtype", [np.int32, np.bool, np.float32, np.int64]) @@ -36,7 +35,6 @@ class TestDataLoader(object): assert np.all((x >= 0) & (x <= 1)) assert np.all((y >= 0) & (y <= 1)) - def test_can_override_shape(self): model = ONNX_MODELS["dynamic_identity"] @@ -49,30 +47,28 @@ class TestDataLoader(object): feed_dict = data_loader[0] assert tuple(feed_dict["X"].shape) == shape - @pytest.mark.parametrize("dtype", [np.int32, np.bool, np.float32, np.int64]) @pytest.mark.parametrize("range_val", [0, 1]) def test_range_min_max_equal(self, dtype, range_val): - data_loader = DataLoader(input_metadata=meta(dtype), - val_range=(range_val, range_val)) + data_loader = DataLoader(input_metadata=meta(dtype), val_range=(range_val, range_val)) feed_dict = data_loader[0] assert np.all(feed_dict["X"] == range_val) assert np.all(feed_dict["Y"] == range_val) - - @pytest.mark.parametrize("range", [ - (0, 1, np.int32), - (5.0, 5.5, np.float32), - (0, 1, np.bool), - ]) + @pytest.mark.parametrize( + "range", + [ + (0, 1, np.int32), + (5.0, 5.5, np.float32), + (0, 1, np.bool), + ], + ) def test_val_ranges(self, range): min_val, max_val, dtype = range - data_loader = DataLoader(input_metadata=meta(dtype), - val_range=(min_val, max_val)) + data_loader = DataLoader(input_metadata=meta(dtype), val_range=(min_val, max_val)) feed_dict = data_loader[0] assert np.all((feed_dict["X"] >= min_val) & (feed_dict["X"] <= max_val)) - @pytest.mark.parametrize("dtype", [np.int32, np.int64, np.float32]) def test_val_range_dict(self, dtype): val_range = {"X": (2, 5), "Y": (-1, 2)} @@ -81,7 +77,6 @@ class TestDataLoader(object): assert np.all((feed_dict["X"] >= 2) & (feed_dict["X"] <= 5)) assert np.all((feed_dict["Y"] >= -1) & (feed_dict["Y"] <= 2)) - @pytest.mark.parametrize("dtype", [np.int32, np.int64, np.float32]) def test_val_range_dict_default(self, dtype): val_range = {"": (6, 8), "Y": (-3, 4)} @@ -90,7 +85,6 @@ class TestDataLoader(object): assert np.all((feed_dict["X"] >= 6) & (feed_dict["X"] <= 8)) assert np.all((feed_dict["Y"] >= -3) & (feed_dict["Y"] <= 4)) - @pytest.mark.parametrize("dtype", [np.int32, np.int64, np.float32]) def test_val_range_dict_fallback(self, dtype): val_range = {"Y": (-3, 4)} @@ -99,53 +93,51 @@ class TestDataLoader(object): assert np.all((feed_dict["X"] >= 0) & (feed_dict["X"] <= 1)) assert np.all((feed_dict["Y"] >= -3) & (feed_dict["Y"] <= 4)) - def test_shape_tensor_detected(self): INPUT_DATA = (1, 2, 3) - input_meta = TensorMetadata().add("X", dtype=np.int32, shape=(3, )) + input_meta = TensorMetadata().add("X", dtype=np.int32, shape=(3,)) # This contains the shape values overriden_meta = TensorMetadata().add("X", dtype=np.int32, shape=INPUT_DATA) data_loader = DataLoader(input_metadata=overriden_meta) data_loader.input_metadata = input_meta feed_dict = data_loader[0] - assert np.all(feed_dict["X"] == INPUT_DATA) # values become INPUT_DATA - + assert np.all(feed_dict["X"] == INPUT_DATA) # values become INPUT_DATA def test_no_shape_tensor_false_positive_negative_dims(self): INPUT_DATA = (-100, 2, 4) # This should NOT be detected as a shape tensor - input_meta = TensorMetadata().add("X", dtype=np.int32, shape=(3, )) + input_meta = TensorMetadata().add("X", dtype=np.int32, shape=(3,)) overriden_meta = TensorMetadata().add("X", dtype=np.int32, shape=INPUT_DATA) data_loader = DataLoader(input_metadata=overriden_meta) data_loader.input_metadata = input_meta feed_dict = data_loader[0] - assert feed_dict["X"].shape == (3, ) # Shape IS (3, ), because this is NOT a shape tensor - assert np.any(feed_dict["X"] != INPUT_DATA) # Contents are not INPUT_DATA, since it's not treated as a shape value - + assert feed_dict["X"].shape == (3,) # Shape IS (3, ), because this is NOT a shape tensor + assert np.any( + feed_dict["X"] != INPUT_DATA + ) # Contents are not INPUT_DATA, since it's not treated as a shape value def test_no_shape_tensor_false_positive_float(self): INPUT_DATA = (-100, -50, 0) # Float cannot be a shape tensor - input_meta = TensorMetadata().add("X", dtype=np.float32, shape=(3, )) + input_meta = TensorMetadata().add("X", dtype=np.float32, shape=(3,)) overriden_meta = TensorMetadata().add("X", dtype=np.float32, shape=INPUT_DATA) data_loader = DataLoader(input_metadata=overriden_meta) data_loader.input_metadata = input_meta feed_dict = data_loader[0] - assert feed_dict["X"].shape == (3, ) # Values are NOT (3, ) - assert np.any(feed_dict["X"] != INPUT_DATA) # Values are NOT (3, ) - + assert feed_dict["X"].shape == (3,) # Values are NOT (3, ) + assert np.any(feed_dict["X"] != INPUT_DATA) # Values are NOT (3, ) def test_non_user_provided_inputs_never_shape_tensors(self): # If the user didn't provide metadata, then the value can never be a shape tensor. - input_meta = TensorMetadata().add("X", dtype=np.int32, shape=(3, )) + input_meta = TensorMetadata().add("X", dtype=np.int32, shape=(3,)) data_loader = DataLoader() data_loader.input_metadata = input_meta feed_dict = data_loader[0] - assert feed_dict["X"].shape == (3, ) # Treat as a normal tensor + assert feed_dict["X"].shape == (3,) # Treat as a normal tensor class TestDataLoaderCache(object): @@ -153,6 +145,7 @@ class TestDataLoaderCache(object): # Ensure that the data loader can only be used once def load_data(): yield {"X": np.ones((1, 1), dtype=np.float32)} + cache = DataLoaderCache(load_data()) fp32_meta = TensorMetadata().add("X", dtype=np.float32, shape=(1, 1)) @@ -165,7 +158,6 @@ class TestDataLoaderCache(object): feed_dict = cache[0] assert feed_dict["X"].dtype == np.float64 - # If one input isn't in the cache, we shouldn't give up looking # for other inputs def test_will_not_give_up_on_first_cache_miss(self): diff --git a/tools/Polygraphy/tests/comparator/test_postprocess.py b/tools/Polygraphy/tests/comparator/test_postprocess.py index 0cbba453..b364e9af 100644 --- a/tools/Polygraphy/tests/comparator/test_postprocess.py +++ b/tools/Polygraphy/tests/comparator/test_postprocess.py @@ -24,14 +24,12 @@ class TestTopK(object): top_k = func(IterationResult({"x": arr})) assert np.all(top_k["x"] == [4, 3, 2]) - def test_k_can_exceed_array_len(self): arr = np.array([1, 2, 3, 4, 5], dtype=np.float32) func = PostprocessFunc.topk_func(k=10) top_k = func(IterationResult({"x": arr})) assert np.all(top_k["x"] == [4, 3, 2, 1, 0]) - def test_per_output_top_k(self): arr = np.array([1, 2, 3, 4, 5], dtype=np.float32) func = PostprocessFunc.topk_func(k={"": 10, "y": 2}) diff --git a/tools/Polygraphy/tests/comparator/test_struct.py b/tools/Polygraphy/tests/comparator/test_struct.py index f9eb09d3..1ad03897 100644 --- a/tools/Polygraphy/tests/comparator/test_struct.py +++ b/tools/Polygraphy/tests/comparator/test_struct.py @@ -1,5 +1,3 @@ - - import numpy as np import pytest import contextlib @@ -16,14 +14,8 @@ def make_iter_results(runner_name): @pytest.fixture(scope="session") def run_results(): results = RunResults() - results.append(( - "runner0", - make_iter_results("runner0") - )) - results.append(( - "runner1", - make_iter_results("runner1") - )) + results.append(("runner0", make_iter_results("runner0"))) + results.append(("runner1", make_iter_results("runner1"))) return results @@ -35,24 +27,20 @@ class TestRunResults(object): for iter_res in iteration_results: assert isinstance(iter_res, IterationResult) - def test_keys(self, run_results): assert list(run_results.keys()) == ["runner0", "runner1"] - def test_values(self, run_results): for iteration_results in run_results.values(): for iter_res in iteration_results: assert isinstance(iter_res, IterationResult) - def test_getitem(self, run_results): assert isinstance(run_results["runner0"][0], IterationResult) assert isinstance(run_results[0][1][0], IterationResult) assert run_results[0][1] == run_results["runner0"] assert run_results[1][1] == run_results["runner1"] - def test_getitem_out_of_bounds(self, run_results): with pytest.raises(IndexError): run_results[2] @@ -60,7 +48,6 @@ class TestRunResults(object): with pytest.raises(PolygraphyException, match="does not exist in this"): run_results["runner2"] - def test_setitem(self, run_results): def check_results(results, is_none=False): for iter_res in results["runner1"]: @@ -78,7 +65,6 @@ class TestRunResults(object): check_results(run_results, is_none=True) - def test_setitem_out_of_bounds(self, run_results): iter_results = [IterationResult(outputs=None, runner_name="new")] run_results["runner2"] = iter_results @@ -86,7 +72,6 @@ class TestRunResults(object): assert len(run_results) == 3 assert run_results["runner2"][0].runner_name == "new" - def test_contains(self, run_results): assert "runner0" in run_results assert "runner1" in run_results @@ -98,13 +83,15 @@ class TestLazyNumpyArray(object): def test_unswapped_array(self, set_threshold): with contextlib.ExitStack() as stack: if set_threshold: + def reset_array_swap(): config.ARRAY_SWAP_THRESHOLD_MB = -1 + stack.callback(reset_array_swap) config.ARRAY_SWAP_THRESHOLD_MB = 8 - small_shape = (7 * 1024 * 1024, ) + small_shape = (7 * 1024 * 1024,) small_array = np.ones(shape=small_shape, dtype=np.byte) lazy = LazyNumpyArray(small_array) assert np.array_equal(small_array, lazy.arr) @@ -112,16 +99,17 @@ class TestLazyNumpyArray(object): assert np.array_equal(small_array, lazy.numpy()) - def test_swapped_array(self): with contextlib.ExitStack() as stack: + def reset_array_swap(): config.ARRAY_SWAP_THRESHOLD_MB = -1 + stack.callback(reset_array_swap) config.ARRAY_SWAP_THRESHOLD_MB = 8 - large_shape = (9 * 1024 * 1024, ) + large_shape = (9 * 1024 * 1024,) large_array = np.ones(shape=large_shape, dtype=np.byte) lazy = LazyNumpyArray(large_array) assert lazy.arr is None diff --git a/tools/Polygraphy/tests/cuda/test_cuda.py b/tools/Polygraphy/tests/cuda/test_cuda.py index de6fa471..829cf790 100644 --- a/tools/Polygraphy/tests/cuda/test_cuda.py +++ b/tools/Polygraphy/tests/cuda/test_cuda.py @@ -29,13 +29,11 @@ class TestDeviceView(object): assert v.dtype == arr.dtype assert v.nbytes == arr.nbytes - def test_with_int_ptr(self): ptr = 74892 - v = DeviceView(ptr=ptr, shape=(1, ), dtype=np.float32) + v = DeviceView(ptr=ptr, shape=(1,), dtype=np.float32) assert v.ptr == ptr - def test_copy_to(self): with DeviceArray((2, 2), dtype=np.float32) as arr: arr.copy_from(np.ones((2, 2), dtype=np.float32) * 4) @@ -46,7 +44,6 @@ class TestDeviceView(object): assert np.all(host_buf == 4) - def test_numpy(self): with DeviceArray((2, 2), dtype=np.float32) as arr: arr.copy_from(np.ones((2, 2), dtype=np.float32) * 4) @@ -63,10 +60,11 @@ class ResizeTestCase(object): self.new = new self.new_bytes = new_size * np.float32().itemsize + RESIZES = [ - ResizeTestCase(tuple(), 1, (1, 1, 1), 1), # Reshape (no-op) - ResizeTestCase((2, 2, 2), 8, (1, 1), 8), # Resize to smaller buffer - ResizeTestCase((2, 2, 2), 8, (9, 9), 81), # Resize to larger buffer + ResizeTestCase(tuple(), 1, (1, 1, 1), 1), # Reshape (no-op) + ResizeTestCase((2, 2, 2), 8, (1, 1), 8), # Resize to smaller buffer + ResizeTestCase((2, 2, 2), 8, (9, 9), 81), # Resize to larger buffer ] @@ -80,16 +78,14 @@ class TestDeviceBuffer(object): assert buf.allocated_nbytes == shapes.new_bytes assert buf.shape == shapes.new - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Breaks TRT 6 tests for some reason") def test_large_allocation(self): dtype = np.byte # See if we can alloc 3GB (bigger than value of signed int) - shape = (3*1024*1024*1024,) + shape = (3 * 1024 * 1024 * 1024,) with DeviceArray(shape=shape, dtype=dtype) as buf: assert buf.allocated_nbytes == util.volume(shape) * np.dtype(dtype).itemsize - def test_device_buffer_memcpy_async(self): arr = np.ones((1, 384), dtype=np.int32) @@ -103,7 +99,6 @@ class TestDeviceBuffer(object): assert np.all(new_arr == arr) - def test_device_buffer_memcpy_sync(self): arr = np.ones((1, 384), dtype=np.int32) @@ -115,7 +110,6 @@ class TestDeviceBuffer(object): assert np.all(new_arr == arr) - def test_device_buffer_free(self): buf = DeviceArray(shape=(64, 64), dtype=np.float32) assert buf.allocated_nbytes == 64 * 64 * np.float32().itemsize @@ -124,7 +118,6 @@ class TestDeviceBuffer(object): assert buf.allocated_nbytes == 0 assert buf.shape == tuple() - def test_empty_tensor_to_host(self): with DeviceArray(shape=(5, 2, 0, 3, 0), dtype=np.float32) as buf: assert util.volume(buf.shape) == 0 diff --git a/tools/Polygraphy/tests/func/test_func.py b/tools/Polygraphy/tests/func/test_func.py index d25b2272..b791a087 100644 --- a/tools/Polygraphy/tests/func/test_func.py +++ b/tools/Polygraphy/tests/func/test_func.py @@ -31,7 +31,6 @@ class TestExtend(object): assert y() == 2 - def test_extend_named_parameters(self): def x(arg0, arg1): return arg0, arg1 @@ -44,7 +43,6 @@ class TestExtend(object): assert arg0 == 0 assert arg1 == 1 - def test_extend_0_args_1_rv(self): def x(): return 1 @@ -55,7 +53,6 @@ class TestExtend(object): assert y() == 1 - def test_extend_0_args_2_rv(self): def x(): return 1, 2 @@ -67,7 +64,6 @@ class TestExtend(object): assert y() == (1, 2) - def test_extend_1_args_0_rv(self): def x(arg0): pass @@ -78,7 +74,6 @@ class TestExtend(object): y(1) - def test_extend_1_args_1_rv(self): def x(arg0): assert arg0 == 1 @@ -90,7 +85,6 @@ class TestExtend(object): assert y(1) == 3 - def test_extend_2_args_2_rv(self): def x(arg0, arg1): assert arg0 == -1 @@ -104,7 +98,6 @@ class TestExtend(object): assert y(-1, -1) == (1, 2) - def test_extend_can_modify_rv(self): def x(): return [] @@ -116,13 +109,11 @@ class TestExtend(object): assert x() == [] assert y() == [1, 2, 3] - def test_extend_can_modify_rv_objects(self): class ModifiableObj(object): def __init__(self): self.value = 0 - def x(): return ModifiableObj() @@ -133,12 +124,14 @@ class TestExtend(object): assert x().value == 0 assert y().value == 1 - def test_extend_incorrect_num_args(self): def x(): return 1, 2 - with pytest.raises(PolygraphyException, match=r"Function: y accepts 1 parameter\(s\), but needs to accept 2 parameter\(s\)"): + with pytest.raises( + PolygraphyException, match=r"Function: y accepts 1 parameter\(s\), but needs to accept 2 parameter\(s\)" + ): + @func.extend(x) def y(elem0): assert elem0 == 1 @@ -160,7 +153,6 @@ class TestConstantMethod(object): with pytest.raises(PolygraphyInternalException, match="was mutated in a constant method"): d.modify_x() - def test_cannot_add_attrs(self): class Dummy(object): @func.constantmethod diff --git a/tools/Polygraphy/tests/helper.py b/tools/Polygraphy/tests/helper.py index 7a901a45..079b5ccb 100644 --- a/tools/Polygraphy/tests/helper.py +++ b/tools/Polygraphy/tests/helper.py @@ -20,5 +20,9 @@ def get_file_size(path): return os.stat(path).st_size -def check_file_non_empty(path): - assert get_file_size(path) > 0 +def is_file_empty(path): + return get_file_size(path) == 0 + + +def is_file_non_empty(path): + return not is_file_empty(path) diff --git a/tools/Polygraphy/tests/mod/test_exporter.py b/tools/Polygraphy/tests/mod/test_exporter.py index d11c30c8..af69db08 100644 --- a/tools/Polygraphy/tests/mod/test_exporter.py +++ b/tools/Polygraphy/tests/mod/test_exporter.py @@ -21,6 +21,7 @@ from polygraphy.backend.base import BaseLoader # For test_funcify_with_collision functor2 = None + class TestExporter(object): def test_func(self): @mod.export() @@ -29,32 +30,31 @@ class TestExporter(object): assert "test_func0" in __all__ - def test_class(self): @mod.export() - class TestClass0(): + class TestClass0: pass assert "TestClass0" in __all__ - def test_funcify_func_fails(self): with pytest.raises(AssertionError, match="must be a loader"): + @mod.export(funcify=True) def test_func1(): pass - def test_funcify_non_base_loader_class(self): with pytest.raises(AssertionError, match="must derive from BaseLoader"): + @mod.export(funcify=True) class NonFunctor0(object): def __init__(self, x): self.x = x - def test_funcify_duplicate_parameters_in_call_init(self): with pytest.raises(AssertionError, match="call_impl and __init__ have the same argument names"): + @mod.export(funcify=True) class DupArgs(BaseLoader): def __init__(self, x): @@ -63,11 +63,11 @@ class TestExporter(object): def call_impl(self, x): self.x = x - def test_funcify_takes_docstring(self): @mod.export(funcify=True) class DocstringFunctor(BaseLoader): """This is a docstring""" + def __init__(self): pass @@ -77,8 +77,7 @@ class TestExporter(object): assert "DocstringFunctor" in __all__ assert "docstring_functor" in __all__ - assert docstring_functor.__doc__ == "Immediately evaluated functional variant of DocstringFunctor.\n" - + assert docstring_functor.__doc__ == "Immediately evaluated functional variant of :class:`DocstringFunctor` .\n" def test_funcify_functor_no_call_args(self): @mod.export(funcify=True) @@ -93,7 +92,6 @@ class TestExporter(object): assert "functor0" in __all__ assert functor0(0) == 0 - def test_funcify_functor_with_call_args(self): @mod.export(funcify=True) class Functor1(BaseLoader): @@ -114,7 +112,6 @@ class TestExporter(object): x, y, z = functor1(y=1, x=0, z=-1) assert (x, y, z) == (0, 1, -1) - def test_funcify_functor_with_call_args(self): @mod.export(funcify=True) class FunctorWithCallArgs(BaseLoader): @@ -135,9 +132,9 @@ class TestExporter(object): x, y, z = functor_with_call_args(y=1) assert (x, y, z) == (0, 1, -1) - def test_funcify_with_collision(self): with pytest.raises(AssertionError, match="symbol is already defined"): + @mod.export(funcify=True) class Functor2(BaseLoader): def __init__(self, x): @@ -146,7 +143,6 @@ class TestExporter(object): def call_impl(self, y, z): return self.x, y, z - def test_funcify_functor_with_dynamic_call_args_kwargs(self): @mod.export(funcify=True) class Functor3(BaseLoader): @@ -166,13 +162,11 @@ class TestExporter(object): assert functor3(f, 1, 2, arg2=4) == 7 - def test_funcify_with_inherited_init(self): class BaseFunctor4(BaseLoader): def __init__(self, x): self.x = x - @mod.export(funcify=True) class Functor4(BaseFunctor4): def call_impl(self): @@ -183,7 +177,6 @@ class TestExporter(object): assert functor4(-1) == -1 - def test_funcify_functor_with_default_vals(self): @mod.export(funcify=True) class FunctorWithDefaults(BaseLoader): @@ -200,8 +193,8 @@ class TestExporter(object): # Since x and z have default values, the arguments will be interlaced into: # w, y, x, z # __init__ parameters take precedence, and call_impl parameters follow. - w, x, y, z = functor_with_defaults(-1, -2) # Set just w, y + w, x, y, z = functor_with_defaults(-1, -2) # Set just w, y assert (w, x, y, z) == (-1, 1, -2, 3) - w, x, y, z = functor_with_defaults(0, 1, 2, 3) # Set all + w, x, y, z = functor_with_defaults(0, 1, 2, 3) # Set all assert (w, x, y, z) == (0, 2, 1, 3) diff --git a/tools/Polygraphy/tests/mod/test_importer.py b/tools/Polygraphy/tests/mod/test_importer.py index c8e799b0..90368dbd 100644 --- a/tools/Polygraphy/tests/mod/test_importer.py +++ b/tools/Polygraphy/tests/mod/test_importer.py @@ -28,7 +28,8 @@ from polygraphy.mod.importer import _version_ok class TestImporter(object): def test_import_from_script(self): - script = dedent(""" + script = dedent( + """ from polygraphy.backend.trt import CreateNetwork from polygraphy import func import tensorrt as trt @@ -38,7 +39,8 @@ class TestImporter(object): inp = network.add_input("input", dtype=trt.float32, shape=(1, 1)) out = network.add_identity(inp).get_output(0) network.mark_output(out) - """) + """ + ) with tempfile.NamedTemporaryFile("w+", suffix=".py") as f: f.write(script) @@ -55,13 +57,13 @@ class TestImporter(object): assert network.get_layer(0).type == trt.LayerType.IDENTITY assert sys.path == orig_sys_path - - def test_import_non_existent(self): - script = dedent(""" + script = dedent( + """ def example(): pass - """) + """ + ) with tempfile.NamedTemporaryFile("w+", suffix=".py") as f: f.write(script) @@ -78,19 +80,21 @@ class TestImporter(object): mod.import_from_script(f.name, "non_existent") assert sys.path == orig_sys_path - - @pytest.mark.parametrize("ver, pref, expected", [ - ("0.0.0", "==0.0.0", True), - ("0.0.0", "== 0.0.1", False), - ("0.0.0", ">= 0.0.0", True), - ("0.0.0", ">=0.0.1", False), - ("0.0.0", "<= 0.0.0", True), - ("0.0.2", "<=0.0.1", False), - ("0.0.1", "> 0.0.0", True), - ("0.0.1", ">0.0.1", False), - ("0.0.0", "< 0.0.1", True), - ("0.0.0", "< 0.0.0", False), - ("0.2.0", mod.LATEST_VERSION, False), - ]) + @pytest.mark.parametrize( + "ver, pref, expected", + [ + ("0.0.0", "==0.0.0", True), + ("0.0.0", "== 0.0.1", False), + ("0.0.0", ">= 0.0.0", True), + ("0.0.0", ">=0.0.1", False), + ("0.0.0", "<= 0.0.0", True), + ("0.0.2", "<=0.0.1", False), + ("0.0.1", "> 0.0.0", True), + ("0.0.1", ">0.0.1", False), + ("0.0.0", "< 0.0.1", True), + ("0.0.0", "< 0.0.0", False), + ("0.2.0", mod.LATEST_VERSION, False), + ], + ) def test_version_ok(self, ver, pref, expected): assert _version_ok(ver, pref) == expected diff --git a/tools/Polygraphy/tests/models/ext_weights_same_dir/ext_weights.data b/tools/Polygraphy/tests/models/ext_weights_same_dir/ext_weights.data new file mode 100644 index 00000000..9decbf8c Binary files /dev/null and b/tools/Polygraphy/tests/models/ext_weights_same_dir/ext_weights.data differ diff --git a/tools/Polygraphy/tests/models/ext_weights_same_dir/ext_weights.onnx b/tools/Polygraphy/tests/models/ext_weights_same_dir/ext_weights.onnx new file mode 100644 index 00000000..ef2d7a87 Binary files /dev/null and b/tools/Polygraphy/tests/models/ext_weights_same_dir/ext_weights.onnx differ diff --git a/tools/Polygraphy/tests/models/make_reducable.py b/tools/Polygraphy/tests/models/make_reducable.py index c3032daf..c5b00586 100644 --- a/tools/Polygraphy/tests/models/make_reducable.py +++ b/tools/Polygraphy/tests/models/make_reducable.py @@ -25,6 +25,7 @@ import onnx_graphsurgeon as gs CURDIR = os.path.dirname(__file__) + @gs.Graph.register() def identity(self, inp): return self.layer(op="Identity", inputs=[inp], outputs=["identity_out"])[0] @@ -44,7 +45,7 @@ def add(self, a, b): # / \ # Z1 Z2 DTYPE = np.float32 -SHAPE = (1, ) +SHAPE = (1,) X0 = gs.Variable("X0", dtype=DTYPE, shape=SHAPE) Y0 = gs.Variable("Y0", dtype=DTYPE, shape=SHAPE) diff --git a/tools/Polygraphy/tests/models/meta.py b/tools/Polygraphy/tests/models/meta.py index 47bb0ce9..cbf03081 100644 --- a/tools/Polygraphy/tests/models/meta.py +++ b/tools/Polygraphy/tests/models/meta.py @@ -65,7 +65,7 @@ def check_identity_identity(runner): def check_dynamic_identity(runner, shapes): feed_dict = {"X": np.random.random_sample(size=shapes["X"]).astype(np.float32)} outputs = runner.infer(feed_dict) - assert np.all(outputs["Y"] == feed_dict["X"]) + assert np.array_equal(outputs["Y"], feed_dict["X"]) def check_empty_tensor_expand(runner, shapes): @@ -88,22 +88,56 @@ def no_check_implemented(runner): ONNX_MODELS = { - "identity": Model(path=model_path("identity.onnx"), LoaderType=BytesFromPath, check_runner=check_identity, - input_metadata=TensorMetadata().add("x", dtype=np.float32, shape=(1, 1, 2, 2))), - "identity_identity": Model(path=model_path("identity_identity.onnx"), LoaderType=BytesFromPath, check_runner=check_identity_identity), - "dynamic_identity": Model(path=model_path("dynamic_identity.onnx"), LoaderType=BytesFromPath, check_runner=check_dynamic_identity, - input_metadata=TensorMetadata().add("X", dtype=np.float32, shape=(1, 1, -1, -1))), - "empty_tensor_expand": Model(path=model_path("empty_tensor_expand.onnx"), LoaderType=BytesFromPath, check_runner=check_empty_tensor_expand), - + "identity": Model( + path=model_path("identity.onnx"), + LoaderType=BytesFromPath, + check_runner=check_identity, + input_metadata=TensorMetadata().add("x", dtype=np.float32, shape=(1, 1, 2, 2)), + ), + "identity_identity": Model( + path=model_path("identity_identity.onnx"), LoaderType=BytesFromPath, check_runner=check_identity_identity + ), + "dynamic_identity": Model( + path=model_path("dynamic_identity.onnx"), + LoaderType=BytesFromPath, + check_runner=check_dynamic_identity, + input_metadata=TensorMetadata().add("X", dtype=np.float32, shape=(1, 1, -1, -1)), + ), + "empty_tensor_expand": Model( + path=model_path("empty_tensor_expand.onnx"), LoaderType=BytesFromPath, check_runner=check_empty_tensor_expand + ), "and": Model(path=model_path("and.onnx"), LoaderType=BytesFromPath, check_runner=no_check_implemented), "scan": Model(path=model_path("scan.onnx"), LoaderType=BytesFromPath, check_runner=no_check_implemented), - "pow_scalar": Model(path=model_path("pow_scalar.onnx"), LoaderType=BytesFromPath, check_runner=no_check_implemented), + "pow_scalar": Model( + path=model_path("pow_scalar.onnx"), LoaderType=BytesFromPath, check_runner=no_check_implemented + ), "dim_param": Model(path=model_path("dim_param.onnx"), LoaderType=BytesFromPath, check_runner=no_check_implemented), - "tensor_attr": Model(path=model_path("tensor_attr.onnx"), LoaderType=BytesFromPath, check_runner=no_check_implemented), - "identity_with_initializer": Model(path=model_path("identity_with_initializer.onnx"), LoaderType=BytesFromPath, check_runner=no_check_implemented), - "const_foldable": Model(path=model_path("const_foldable.onnx"), LoaderType=BytesFromPath, check_runner=no_check_implemented), + "tensor_attr": Model( + path=model_path("tensor_attr.onnx"), LoaderType=BytesFromPath, check_runner=no_check_implemented + ), + "identity_with_initializer": Model( + path=model_path("identity_with_initializer.onnx"), LoaderType=BytesFromPath, check_runner=no_check_implemented + ), + "const_foldable": Model( + path=model_path("const_foldable.onnx"), LoaderType=BytesFromPath, check_runner=no_check_implemented + ), "reshape": Model(path=model_path("reshape.onnx"), LoaderType=BytesFromPath, check_runner=check_reshape), - "reducable": Model(path=model_path("reducable.onnx"), LoaderType=BytesFromPath, check_runner=no_check_implemented, - input_metadata=TensorMetadata().add("X0", shape=(1, ), dtype=np.float32).add("Y0", shape=(1, ), dtype=np.float32)), - "ext_weights": Model(path=model_path("ext_weights.onnx"), LoaderType=OnnxFromPath, check_runner=no_check_implemented, ext_data=model_path("data")), + "reducable": Model( + path=model_path("reducable.onnx"), + LoaderType=BytesFromPath, + check_runner=no_check_implemented, + input_metadata=TensorMetadata().add("X0", shape=(1,), dtype=np.float32).add("Y0", shape=(1,), dtype=np.float32), + ), + "ext_weights": Model( + path=model_path("ext_weights.onnx"), + LoaderType=OnnxFromPath, + check_runner=no_check_implemented, + ext_data=model_path("data"), + ), + "ext_weights_same_dir": Model( + path=model_path(os.path.join("ext_weights_same_dir", "ext_weights.onnx")), + LoaderType=OnnxFromPath, + check_runner=no_check_implemented, + ext_data=model_path("ext_weights_same_dir"), + ), } diff --git a/tools/Polygraphy/tests/test_deprecated_aliases.py b/tools/Polygraphy/tests/test_deprecated_aliases.py index 920bdaa3..9a1689ca 100644 --- a/tools/Polygraphy/tests/test_deprecated_aliases.py +++ b/tools/Polygraphy/tests/test_deprecated_aliases.py @@ -14,51 +14,57 @@ # limitations under the License. # + class TestOnnxLoaders(object): def test_modify_onnx(self): from polygraphy.backend.onnx import ModifyOnnx + ModifyOnnx(None) class TestOnnxrtLoaders(object): def test_session_from_onnx_bytes(self): from polygraphy.backend.onnxrt import SessionFromOnnxBytes + SessionFromOnnxBytes(None) class TestTrtLoaders(object): def test_modify_network(self): from polygraphy.backend.trt import ModifyNetwork + ModifyNetwork(None) class TestTfLoaders(object): def test_modify_network(self): from polygraphy.backend.tf import ModifyGraph + ModifyGraph(None) class TestUtil(object): def test_misc(self): from polygraphy.util import misc - assert misc.default(None, 1) == 1 + assert misc.default(None, 1) == 1 def test_default_value(self): from polygraphy import util - assert util.default_value(None, 1) == 1 + assert util.default_value(None, 1) == 1 def test_pickle_load(self): from polygraphy.util import pickle_load + try: assert pickle_load(None) is None except: pass - def test_pickle_save(self): from polygraphy.util import pickle_save + try: assert pickle_save(None, None) is None except: @@ -68,33 +74,36 @@ class TestUtil(object): class TestCuda(object): def test_cuda(self): from polygraphy.common import cuda + assert cuda.DeviceArray class TestFunc(object): def test_func(self): from polygraphy.common import func + assert hasattr(func, "extend") class TestException(object): def test_exception(self): from polygraphy.common import exception + assert hasattr(exception, "PolygraphyException") class TestConstants(object): def test_constants(self): from polygraphy.common import constants - assert constants.MARK_ALL + assert constants.MARK_ALL def test_config(self): from polygraphy import constants + assert (constants.INTERNAL_CORRECTNESS_CHECKS, constants.AUTOINSTALL_DEPS) class TestUtilJson(object): def test_json(self): - from polygraphy.util import (Decoder, Encoder, from_json, load_json, - save_json, to_json) + from polygraphy.util import Decoder, Encoder, from_json, load_json, save_json, to_json diff --git a/tools/Polygraphy/tests/test_deps.py b/tools/Polygraphy/tests/test_deps.py index e49b1823..4634c20b 100644 --- a/tools/Polygraphy/tests/test_deps.py +++ b/tools/Polygraphy/tests/test_deps.py @@ -37,21 +37,28 @@ def virtualenv_with_poly(virtualenv): def is_submodule(path): - return os.path.isdir(path) and os.path.isfile(os.path.join(path, "__init__.py")) + file_mod = os.path.isfile(path) and path.endswith(".py") and os.path.basename(path) != "__init__.py" + dir_mod = os.path.isdir(path) and os.path.isfile(os.path.join(path, "__init__.py")) + return file_mod or dir_mod + MODULE_PATH = os.path.join(ROOT_DIR, "polygraphy") -SUBMODULE_PATHS = [os.path.relpath(path, ROOT_DIR) for path in glob.iglob(os.path.join(MODULE_PATH, "**"), recursive=True) if is_submodule(path)] +SUBMODULE_PATHS = [ + os.path.relpath(os.path.splitext(path)[0], ROOT_DIR) + for path in glob.iglob(os.path.join(MODULE_PATH, "**"), recursive=True) + if is_submodule(path) +] + class TestPublicImports(object): - # Submodules should not require any extra dependencies to import. - @pytest.mark.parametrize("submodule_path", SUBMODULE_PATHS) - def test_no_extra_submodule_dependencies_required(self, virtualenv_with_poly, submodule_path): - submodule_name = ".".join(submodule_path.split(os.path.sep)) - cmd = ["python3", "-c", "from {:} import *".format(submodule_name)] - print(" ".join(cmd)) - output = virtualenv_with_poly.run(cmd, capture=True) - print(output) - + def test_no_extra_submodule_dependencies_required(self, virtualenv_with_poly): + # Submodules should not require any extra dependencies to import. + for submodule_path in SUBMODULE_PATHS: + submodule_name = ".".join(submodule_path.split(os.path.sep)) + cmd = ["python3", "-c", "from {:} import *".format(submodule_name)] + print(" ".join(cmd)) + output = virtualenv_with_poly.run(cmd, capture=True) + print(output) def test_can_json_without_numpy(self, virtualenv_with_poly): cmd = ["python3", "-c", "from polygraphy.json import to_json, from_json; x = to_json(1); x = from_json(x)"] @@ -70,6 +77,7 @@ TOOLS = { "debug": ["build", "precision", "diff-tactics", "reduce", "repeat"], } + class TestToolImports(object): # We should be able to at least launch tools with no dependencies installed. @pytest.mark.parametrize("tool, subtools", TOOLS.items()) @@ -90,12 +98,21 @@ class TestToolImports(object): class TestAutoinstallDeps(object): - @pytest.mark.parametrize("cmd", [ - ["run", ONNX_MODELS["identity"].path, "--onnxrt"], - ["run", ONNX_MODELS["identity"].path, "--trt"], - ["surgeon", "sanitize", "--fold-constants", ONNX_MODELS["const_foldable"].path, - "-o", tempfile.NamedTemporaryFile().name], - ]) + @pytest.mark.parametrize( + "cmd", + [ + ["run", ONNX_MODELS["identity"].path, "--onnxrt"], + ["run", ONNX_MODELS["identity"].path, "--trt"], + [ + "surgeon", + "sanitize", + "--fold-constants", + ONNX_MODELS["const_foldable"].path, + "-o", + tempfile.NamedTemporaryFile().name, + ], + ], + ) def test_can_automatically_install_deps(self, virtualenv_with_poly, cmd): if "--trt" in cmd and mod.version(trt.__version__) < mod.version("7.0"): pytest.skip("TRT 6 container has an old version of CUDA") @@ -106,11 +123,13 @@ class TestAutoinstallDeps(object): print(output) assert "is required, but not installed. Attempting to install now" in output - - @pytest.mark.parametrize("new_ver, expected", [ - ("==1.4.2", "==1.4.2"), - (mod.LATEST_VERSION, ">=1.4.2"), - ]) + @pytest.mark.parametrize( + "new_ver, expected", + [ + ("==1.4.2", "==1.4.2"), + (mod.LATEST_VERSION, ">=1.4.2"), + ], + ) def test_can_automatically_upgrade_deps(self, virtualenv_with_poly, new_ver, expected): virtualenv_with_poly.env["POLYGRAPHY_AUTOINSTALL_DEPS"] = "1" @@ -121,12 +140,32 @@ class TestAutoinstallDeps(object): assert get_colored_version() == "1.4.0" # Insert our own preferred version to make sure it upgrades. - virtualenv_with_poly.run(["python3", "-c", - "from polygraphy import mod; " - "colored = mod.lazy_import('colored', version='{:}'); " - "print(colored.__version__)".format(new_ver)]) + virtualenv_with_poly.run( + [ + "python3", + "-c", + "from polygraphy import mod; " + "colored = mod.lazy_import('colored', version='{:}'); " + "print(colored.__version__)".format(new_ver), + ] + ) assert _version_ok(get_colored_version(), expected) + # We can import inner modules, and Polygraphy should still autoinstall the outermost one. + def test_can_install_for_nested_import(self, virtualenv_with_poly): + virtualenv_with_poly.env["POLYGRAPHY_AUTOINSTALL_DEPS"] = "1" + + virtualenv_with_poly.run( + [ + "python3", + "-c", + "from polygraphy import mod; " + "shape_inference = mod.lazy_import('onnx.shape_inference'); " + "print(shape_inference.infer_shapes)", + ] + ) + + assert "onnx" in virtualenv_with_poly.installed_packages() def test_all_lazy_imports(self): # NOTE: If this test fails, it means a new lazy dependency has been diff --git a/tools/Polygraphy/tests/test_examples.py b/tools/Polygraphy/tests/test_examples.py index 82d4b928..76333abb 100644 --- a/tools/Polygraphy/tests/test_examples.py +++ b/tools/Polygraphy/tests/test_examples.py @@ -33,7 +33,7 @@ def load_code_blocks_from_readme(readme, ignore_block): return "pip" in cmd commands = [] - with open(readme, 'r') as f: + with open(readme, "r") as f: in_command_block = False block = [] for line in f.readlines(): @@ -59,12 +59,10 @@ class Example(object): self.artifacts = [os.path.join(self.path, name) for name in artifact_names] self.ignore_block = util.default(ignore_block, lambda block: False) - def __enter__(self): readme = os.path.join(self.path, "README.md") return load_code_blocks_from_readme(readme, self.ignore_block) - def run(self, command): G_LOGGER.info("Running: {:} from cwd: {:}".format(command, self.path)) env = copy.copy(os.environ) @@ -79,7 +77,6 @@ class Example(object): assert status.returncode == 0, status.stdout + "\n" + status.stderr return status - def __exit__(self, exc_type, exc_value, traceback): """ Checks for and removes artifacts expected by this example @@ -92,7 +89,6 @@ class Example(object): else: os.remove(artifact) - def __str__(self): return os.path.relpath(self.path, EXAMPLES_ROOT) @@ -105,11 +101,20 @@ API_EXAMPLES = [ Example(["api", "04_int8_calibration_in_tensorrt"], artifact_names=["identity-calib.cache"]), Example(["api", "05_using_tensorrt_network_api"]), Example(["api", "06_immediate_eval_api"], ignore_block=lambda block: "```python" in block[0]), + Example( + ["api", "07_tensorrt_and_dynamic_shapes"], + artifact_names=["dynamic_identity.engine"], + ignore_block=lambda block: "```python" in block[0], + ), ] + @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") @pytest.mark.parametrize("example", API_EXAMPLES, ids=lambda case: str(case)) def test_api_examples(example): + if mod.version(trt.__version__) < mod.version("8.0") and (example.path.endswith("07_tensorrt_and_dynamic_shapes")): + pytest.skip("Not intended for older versions of TRT") + with example as commands: for command in commands: example.run(command) @@ -120,9 +125,14 @@ CLI_EXAMPLES = [ Example(["cli", "run", "01_comparing_frameworks"]), Example(["cli", "run", "02_comparing_across_runs"], artifact_names=["system_a_results.json"]), Example(["cli", "run", "03_generating_a_comparison_script"], artifact_names=["compare_trt_onnxrt.py"]), - Example(["cli", "run", "04_defining_a_trt_network_manually"]), + Example(["cli", "run", "04_defining_a_tensorrt_network_manually"]), # Convert Example(["cli", "convert", "01_int8_calibration_in_tensorrt"], artifact_names=["identity.engine"]), + Example( + ["cli", "convert", "02_deterministic_engine_builds_in_tensorrt"], + artifact_names=["0.engine", "1.engine", "replay.json"], + ), + Example(["cli", "convert", "03_dynamic_shapes_in_tensorrt"], artifact_names=["dynamic_identity.engine"]), # Surgeon Example(["cli", "surgeon", "01_isolating_subgraphs"], artifact_names=["subgraph.onnx"]), Example(["cli", "surgeon", "02_folding_constants"], artifact_names=["folded.onnx"]), @@ -130,10 +140,14 @@ CLI_EXAMPLES = [ Example(["cli", "debug", "01_debugging_flaky_trt_tactics"], artifact_names=["replays", "golden.json"]), ] + @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") @pytest.mark.parametrize("example", CLI_EXAMPLES, ids=lambda case: str(case)) def test_cli_examples(example): - if mod.version(trt.__version__) < mod.version("8.0") and example.path.endswith("01_debugging_flaky_trt_tactics"): + if mod.version(trt.__version__) < mod.version("8.0") and ( + example.path.endswith("01_debugging_flaky_trt_tactics") + or example.path.endswith("02_deterministic_engine_builds_in_tensorrt") + ): pytest.skip("Tactic replays are not supported on older versions of TRT") with example as commands: @@ -155,6 +169,7 @@ if mod.version(trt.__version__) >= mod.version("8.0"): Example(["cli", "inspect", "07_inspecting_tactic_replays"], artifact_names=["replay.json"]), ] + @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") @pytest.mark.parametrize("example", CLI_INSPECT_EXAMPLES, ids=lambda case: str(case)) def test_cli_inspect_examples(example): @@ -166,7 +181,8 @@ def test_cli_inspect_examples(example): print(actual_output) # Makes reading the diff way easier - actual_lines = [line for line in actual_output.splitlines() if "[I] Loading " not in line] + actual_lines = [line for line in actual_output.splitlines() if "[I] Loading " not in line and "[W] " not in line] + expected_lines = expected_output.splitlines() assert len(actual_lines) == len(expected_lines) @@ -184,6 +200,7 @@ DEV_EXAMPLES = [ Example(["dev", "01_writing_cli_tools"], artifact_names=["data.json"]), ] + @pytest.mark.parametrize("example", DEV_EXAMPLES, ids=lambda case: str(case)) def test_dev_examples(example): with example as commands: diff --git a/tools/Polygraphy/tests/test_packaging.py b/tools/Polygraphy/tests/test_packaging.py index 0e609b8b..ef7e9a2a 100644 --- a/tools/Polygraphy/tests/test_packaging.py +++ b/tools/Polygraphy/tests/test_packaging.py @@ -44,9 +44,24 @@ class TestWheel(object): all_poly_files = [f for f in map(os.path.basename, all_poly_files) if f not in EXCLUDE_FILES] # NOTE: This should be updated when new files are added to the top-level package. - EXPECTED_FILES = set(["backend", "mod", "__init__.py", "cuda", "logger", "constants.py", - "util", "comparator", "tools", "exception", "func", "common", "json", - "config.py"]) + EXPECTED_FILES = set( + [ + "backend", + "mod", + "__init__.py", + "cuda", + "logger", + "constants.py", + "util", + "comparator", + "tools", + "exception", + "func", + "common", + "json", + "config.py", + ] + ) assert set(all_poly_files) == EXPECTED_FILES # Check CLI is installed @@ -58,4 +73,4 @@ class TestWheel(object): lib_path = virtualenv.virtualenv.dirs()[0] output = virtualenv.run(["polygraphy", "-v"], capture=True) assert polygraphy.__version__ in output - assert lib_path in output # Make sure we're using the binary from the venv. + assert lib_path in output # Make sure we're using the binary from the venv. diff --git a/tools/Polygraphy/tests/tools/args/helper.py b/tools/Polygraphy/tests/tools/args/helper.py index 81929998..6652112c 100644 --- a/tools/Polygraphy/tests/tools/args/helper.py +++ b/tools/Polygraphy/tests/tools/args/helper.py @@ -28,13 +28,13 @@ class ArgGroupTestHelper(object): for other_dep in self.deps: other_dep.register(dep) self.arg_group.register(dep) + dep.register(self.arg_group) self.arg_group.check_registered() for dep in self.deps: dep.add_to_parser(self.parser) self.arg_group.add_to_parser(self.parser) - def parse_args(self, cli_args): args = self.parser.parse_args(cli_args) for dep in self.deps: @@ -42,7 +42,6 @@ class ArgGroupTestHelper(object): self.arg_group.parse(args) return args - def __getattr__(self, name): if name in ["arg_group", "parser"]: return super().__getattr__(name) diff --git a/tools/Polygraphy/tests/tools/args/onnx/test_loader.py b/tools/Polygraphy/tests/tools/args/onnx/test_loader.py index b848b8a8..df1e124e 100644 --- a/tools/Polygraphy/tests/tools/args/onnx/test_loader.py +++ b/tools/Polygraphy/tests/tools/args/onnx/test_loader.py @@ -14,16 +14,24 @@ # limitations under the License. # +import glob +import os import tempfile from polygraphy.backend.onnx import onnx_from_path -from polygraphy.tools.args import (DataLoaderArgs, ModelArgs, OnnxLoaderArgs, - OnnxSaveArgs, OnnxShapeInferenceArgs) -from tests.helper import check_file_non_empty +from polygraphy.tools.args import DataLoaderArgs, ModelArgs, OnnxLoaderArgs, OnnxSaveArgs, OnnxShapeInferenceArgs +from polygraphy.tools.script import Script +from tests.helper import is_file_empty, is_file_non_empty from tests.models.meta import ONNX_MODELS from tests.tools.args.helper import ArgGroupTestHelper +def _check_ext_weights_model(model): + assert len(model.graph.node) == 3 + for init in model.graph.initializer: + assert init + + class TestOnnxLoaderArgs(object): def test_basic(self): arg_group = ArgGroupTestHelper(OnnxLoaderArgs(), deps=[ModelArgs()]) @@ -33,14 +41,42 @@ class TestOnnxLoaderArgs(object): assert len(model.graph.output) == 1 assert model.graph.output[0].name == "identity_out_0" - def test_external_data(self): arg_group = ArgGroupTestHelper(OnnxLoaderArgs(), deps=[ModelArgs()]) model = ONNX_MODELS["ext_weights"] - arg_group.parse_args([model.path, "--load-external-data", model.ext_data]) + arg_group.parse_args([model.path, "--external-data-dir", model.ext_data]) model = arg_group.load_onnx() + _check_ext_weights_model(model) - assert len(model.graph.node) == 3 + def test_shape_inference(self): + # When using shape inference, we should load directly from the path + arg_group = ArgGroupTestHelper(OnnxLoaderArgs(), deps=[ModelArgs(), OnnxShapeInferenceArgs()]) + model = ONNX_MODELS["identity"] + arg_group.parse_args([model.path, "--shape-inference"]) + + assert arg_group.should_use_onnx_loader() + + script = Script() + arg_group.add_onnx_loader(script) + + expected_loader = "InferShapes({:})".format(repr(model.path)) + assert expected_loader in str(script) + + def test_shape_inference_ext_data(self): + arg_group = ArgGroupTestHelper(OnnxLoaderArgs(), deps=[ModelArgs(), OnnxShapeInferenceArgs()]) + model = ONNX_MODELS["ext_weights"] + arg_group.parse_args([model.path, "--external-data-dir", model.ext_data, "--shape-inference"]) + + assert arg_group.should_use_onnx_loader() + + script = Script() + arg_group.add_onnx_loader(script) + + expected_loader = "InferShapes({:}, external_data_dir={:})".format(repr(model.path), repr(model.ext_data)) + assert expected_loader in str(script) + + model = arg_group.load_onnx() + _check_ext_weights_model(model) class TestOnnxSaveArgs(object): @@ -48,16 +84,52 @@ class TestOnnxSaveArgs(object): model = onnx_from_path(ONNX_MODELS["const_foldable"].path) arg_group = ArgGroupTestHelper(OnnxSaveArgs(), deps=[ModelArgs(), OnnxLoaderArgs()]) with tempfile.NamedTemporaryFile() as path, tempfile.NamedTemporaryFile() as data: - arg_group.parse_args(["-o", path.name, "--save-external-data", data.name]) + arg_group.parse_args( + ["-o", path.name, "--save-external-data", data.name, "--external-data-size-threshold=0"] + ) arg_group.save_onnx(model) - check_file_non_empty(path.name) - check_file_non_empty(data.name) + assert is_file_non_empty(path.name) + assert is_file_non_empty(data.name) + + def test_size_threshold(self): + model = onnx_from_path(ONNX_MODELS["const_foldable"].path) + arg_group = ArgGroupTestHelper(OnnxSaveArgs(), deps=[ModelArgs(), OnnxLoaderArgs()]) + with tempfile.NamedTemporaryFile() as path, tempfile.NamedTemporaryFile() as data: + arg_group.parse_args( + ["-o", path.name, "--save-external-data", data.name, "--external-data-size-threshold=1024"] + ) + arg_group.save_onnx(model) + + assert is_file_non_empty(path.name) + assert is_file_empty(data.name) + + def test_no_all_tensors_to_one_file(self): + model = onnx_from_path(ONNX_MODELS["const_foldable"].path) + arg_group = ArgGroupTestHelper(OnnxSaveArgs(), deps=[ModelArgs(), OnnxLoaderArgs()]) + with tempfile.TemporaryDirectory() as outdir: + path = os.path.join(outdir, "model.onnx") + arg_group.parse_args( + [ + "-o", + path, + "--save-external-data", + "--external-data-size-threshold=0", + "--no-save-all-tensors-to-one-file", + ] + ) + arg_group.save_onnx(model) + + assert is_file_non_empty(path) + outfiles = glob.glob(os.path.join(outdir, "*")) + assert len(outfiles) == 4 class TestOnnxShapeInferenceArgs(object): def test_shape_inference_disabled_on_fallback(self): - arg_group = ArgGroupTestHelper(OnnxShapeInferenceArgs(default=True, enable_force_fallback=True), deps=[DataLoaderArgs()]) + arg_group = ArgGroupTestHelper( + OnnxShapeInferenceArgs(default=True, enable_force_fallback=True), deps=[DataLoaderArgs()] + ) arg_group.parse_args([]) assert arg_group.do_shape_inference diff --git a/tools/Polygraphy/tests/tools/args/test_comparator.py b/tools/Polygraphy/tests/tools/args/test_comparator.py index de7c9d26..360df7a5 100644 --- a/tools/Polygraphy/tests/tools/args/test_comparator.py +++ b/tools/Polygraphy/tests/tools/args/test_comparator.py @@ -28,24 +28,26 @@ class TestComparatorCompare(object): assert arg_group.check_error_stat == {"": check_error_stat} - - @pytest.mark.parametrize("args, expected", [ - (["mean", "output0:median", "output1:max"], - {"": "mean", "output0": "median", "output1": "max"}), - (["output0:median", "output1:elemwise"], - {"output0": "median", "output1": "elemwise"}), - ]) + @pytest.mark.parametrize( + "args, expected", + [ + (["mean", "output0:median", "output1:max"], {"": "mean", "output0": "median", "output1": "max"}), + (["output0:median", "output1:elemwise"], {"output0": "median", "output1": "elemwise"}), + ], + ) def test_error_stat_per_output(self, args, expected): arg_group = ArgGroupTestHelper(ComparatorCompareArgs()) arg_group.parse_args(["--check-error-stat"] + args) assert arg_group.check_error_stat == expected - - @pytest.mark.parametrize("args", [ - ["not-a-stat"], - ["output0:fake"], - ]) + @pytest.mark.parametrize( + "args", + [ + ["not-a-stat"], + ["output0:fake"], + ], + ) def test_invalid_error_stat(self, args): with pytest.raises(PolygraphyException, match="Invalid choice"): arg_group = ArgGroupTestHelper(ComparatorCompareArgs()) diff --git a/tools/Polygraphy/tests/tools/args/test_data_loader.py b/tools/Polygraphy/tests/tools/args/test_data_loader.py index f16183c0..d043f1e6 100644 --- a/tools/Polygraphy/tests/tools/args/test_data_loader.py +++ b/tools/Polygraphy/tests/tools/args/test_data_loader.py @@ -29,15 +29,20 @@ ARG_CASES = [ (["--seed=123"], ["seed"], [123]), (["--int-min=23", "--int-max=94"], ["int_range"], [(23, 94)]), (["--float-min=2.3", "--float-max=9.4"], ["float_range"], [(2.3, 9.4)]), - ([], ["val_range"], [None], [(0.0, 1.0)]), # When not specified, this should default to None. + ([], ["val_range"], [None], [(0.0, 1.0)]), # When not specified, this should default to None. (["--val-range", "[0.0,2.3]"], ["val_range"], [{"": (0.0, 2.3)}]), (["--val-range", "inp0:[0.0,2.3]", "inp1:[4.5,9.6]"], ["val_range"], [{"inp0": (0.0, 2.3), "inp1": (4.5, 9.6)}]), - (["--val-range", "[-1,0]", "inp0:[0.0,2.3]", "inp1:[4.5,9.6]"], ["val_range"], [{"": (-1, 0), "inp0": (0.0, 2.3), "inp1": (4.5, 9.6)}]), + ( + ["--val-range", "[-1,0]", "inp0:[0.0,2.3]", "inp1:[4.5,9.6]"], + ["val_range"], + [{"": (-1, 0), "inp0": (0.0, 2.3), "inp1": (4.5, 9.6)}], + ), (["--val-range", "))):[0.0,2.3]"], ["val_range"], [{")))": (0.0, 2.3)}]), (["--val-range", "'\"':[0.0,2.3]"], ["val_range"], [{"'\"'": (0.0, 2.3)}]), (["--iterations=12"], ["iterations"], [12]), ] + class TestDataLoaderArgs(object): @pytest.mark.parametrize("case", ARG_CASES, ids=lambda c: c[1][0]) def test_parsing(self, case): @@ -52,7 +57,6 @@ class TestDataLoaderArgs(object): assert getattr(arg_group, attr) == exp assert getattr(data_loader, attr) == exp_dl - def test_input_metadata(self): arg_group = ArgGroupTestHelper(DataLoaderArgs(), deps=[ModelArgs()]) arg_group.parse_args(["--input-shapes", "test0:[1,1,1]", "test1:[2,32,2]"]) @@ -62,27 +66,31 @@ class TestDataLoaderArgs(object): assert feed_dict["test0"].shape == (1, 1, 1) assert feed_dict["test1"].shape == (2, 32, 2) - def test_override_input_metadata(self): arg_group = ArgGroupTestHelper(DataLoaderArgs(), deps=[ModelArgs()]) arg_group.parse_args([]) - data_loader = arg_group.get_data_loader(user_input_metadata=TensorMetadata().add("test0", dtype=np.float32, shape=(4, 4))) + data_loader = arg_group.get_data_loader( + user_input_metadata=TensorMetadata().add("test0", dtype=np.float32, shape=(4, 4)) + ) for feed_dict in data_loader: assert feed_dict["test0"].shape == (4, 4) - def test_data_loader_script(self): arg_group = ArgGroupTestHelper(DataLoaderArgs()) with tempfile.NamedTemporaryFile("w+", suffix=".py") as f: - f.write(dedent(""" - import numpy as np + f.write( + dedent( + """ + import numpy as np - def my_load_data(): - for _ in range(5): - yield {"inp": np.ones((3, 5), dtype=np.float32) * 6.4341} - """)) + def my_load_data(): + for _ in range(5): + yield {"inp": np.ones((3, 5), dtype=np.float32) * 6.4341} + """ + ) + ) f.flush() arg_group.parse_args(["--data-loader-script", f.name, "--data-loader-func-name=my_load_data"]) diff --git a/tools/Polygraphy/tests/tools/args/test_logger.py b/tools/Polygraphy/tests/tools/args/test_logger.py index b73f53c0..4e98a615 100644 --- a/tools/Polygraphy/tests/tools/args/test_logger.py +++ b/tools/Polygraphy/tests/tools/args/test_logger.py @@ -14,11 +14,13 @@ # limitations under the License. # +import os +import tempfile + +import pytest from polygraphy.logger.logger import G_LOGGER from polygraphy.tools.args import LoggerArgs from tests.tools.args.helper import ArgGroupTestHelper -import pytest - VERBOSITY_CASES = { "--silent": G_LOGGER.CRITICAL, @@ -33,6 +35,7 @@ VERBOSITY_CASES = { "-vvvv": G_LOGGER.ULTRA_VERBOSE, } + class TestLoggerArgs(object): @pytest.mark.parametrize("case", VERBOSITY_CASES.items()) def test_get_logger_verbosities(self, case): @@ -44,10 +47,11 @@ class TestLoggerArgs(object): assert logger.severity == sev - def test_logger_log_file(self): arg_group = ArgGroupTestHelper(LoggerArgs()) - arg_group.parse_args(["--log-file=fake_log_file.log"]) - logger = arg_group.get_logger() - assert logger.log_file == "fake_log_file.log" + with tempfile.TemporaryDirectory() as dirname: + log_path = os.path.join(dirname, "fake_log_file.log") + arg_group.parse_args(["--log-file", log_path]) + logger = arg_group.get_logger() + assert logger.log_file == log_path diff --git a/tools/Polygraphy/tests/tools/args/test_model.py b/tools/Polygraphy/tests/tools/args/test_model.py index 4f5d6108..855dfe15 100644 --- a/tools/Polygraphy/tests/tools/args/test_model.py +++ b/tools/Polygraphy/tests/tools/args/test_model.py @@ -38,11 +38,17 @@ class TestModelArgs(object): assert group.model_file == os.path.abspath("model.onnx") assert group.model_type.is_onnx() - def test_input_shapes(self, group): group.parse_args(["--input-shapes", "test0:[1,1]", "test1:[10]", "test:2:[25,301]", "test3:[]"]) assert group.input_shapes["test0"].shape == (1, 1) - assert group.input_shapes["test1"].shape == (10, ) + assert group.input_shapes["test1"].shape == (10,) assert group.input_shapes["test:2"].shape == (25, 301) assert group.input_shapes["test3"].shape == tuple() + + + def test_fixed_model_type(self): + group = ArgGroupTestHelper(ModelArgs(model_type="onnx")) + group.parse_args(["model.pb"]) + + assert group.model_type.is_onnx() diff --git a/tools/Polygraphy/tests/tools/args/test_util.py b/tools/Polygraphy/tests/tools/args/test_util.py index 4ed523d9..a2e3640a 100644 --- a/tools/Polygraphy/tests/tools/args/test_util.py +++ b/tools/Polygraphy/tests/tools/args/test_util.py @@ -23,38 +23,33 @@ from polygraphy.tools.script import inline, safe @pytest.mark.parametrize("name", ["input", "input:0"]) class TestParseMeta(object): - def test_parse_legacy(self, name): # Legacy argument format used comma. + def test_parse_legacy(self, name): # Legacy argument format used comma. meta_args = ["{:},1x3x224x224".format(name)] meta = args_util.parse_meta(meta_args, includes_dtype=False) assert meta[name].shape == (1, 3, 224, 224) assert meta[name].dtype is None - def test_parse_shape_only(self, name): meta_args = ["{name}:[1,3,224,224]".format(name=name)] meta = args_util.parse_meta(meta_args, includes_dtype=False) assert meta[name].shape == (1, 3, 224, 224) assert meta[name].dtype is None - def test_parse_empty_shape(self, name): meta_args = ["{name}:[0,3,0,224]".format(name=name)] meta = args_util.parse_meta(meta_args, includes_dtype=False) assert meta[name].shape == (0, 3, 0, 224) assert meta[name].dtype is None - def test_parse_shape_scalar(self, name): meta_args = ["{name}:[]".format(name=name)] meta = args_util.parse_meta(meta_args, includes_dtype=False) assert meta[name].shape == tuple() - def test_parse_shape_single_dim(self, name): meta_args = ["{name}:[1]".format(name=name)] meta = args_util.parse_meta(meta_args, includes_dtype=False) - assert meta[name].shape == (1, ) - + assert meta[name].shape == (1,) def test_parse_dtype_only(self, name): meta_args = ["{name}:float32".format(name=name)] @@ -62,22 +57,19 @@ class TestParseMeta(object): assert meta[name].shape is None assert meta[name].dtype == np.float32 - def test_parse_shape_dtype(self, name): meta_args = ["{name}:[1,3,224,224]:float32".format(name=name)] meta = args_util.parse_meta(meta_args) assert meta[name].shape == (1, 3, 224, 224) assert meta[name].dtype == np.float32 - def test_parse_shape_dtype_auto(self, name): meta_args = ["{name}:auto:auto".format(name=name)] meta = args_util.parse_meta(meta_args) assert meta[name].shape is None assert meta[name].dtype is None - - @pytest.mark.parametrize("quote", ["\"", "'", ""]) + @pytest.mark.parametrize("quote", ['"', "'", ""]) def test_parse_shape_with_dim_param_quoted(self, name, quote): meta_args = ["{name}:[{quote}batch{quote},3,224,224]".format(name=name, quote=quote)] meta = args_util.parse_meta(meta_args, includes_dtype=False) diff --git a/tools/Polygraphy/tests/tools/args/trt/test_config.py b/tools/Polygraphy/tests/tools/args/trt/test_config.py index aa1a8c66..15f6d59e 100644 --- a/tools/Polygraphy/tests/tools/args/trt/test_config.py +++ b/tools/Polygraphy/tests/tools/args/trt/test_config.py @@ -21,8 +21,7 @@ from textwrap import dedent import pytest import tensorrt as trt from polygraphy import mod -from polygraphy.backend.trt import (TacticRecorder, TacticReplayer, - create_network) +from polygraphy.backend.trt import TacticRecorder, TacticReplayer, create_network from polygraphy.exception import PolygraphyException from polygraphy.tools.args import DataLoaderArgs, ModelArgs, TrtConfigArgs from tests.tools.args.helper import ArgGroupTestHelper @@ -41,12 +40,14 @@ class TestTrtConfigArgs(object): with builder, network, trt_config_args.create_config(builder, network=network) as config: assert isinstance(config, trt.IBuilderConfig) - - @pytest.mark.parametrize("arg, flag", [ - ("--int8", "INT8"), - ("--fp16", "FP16"), - ("--tf32", "TF32"), - ]) + @pytest.mark.parametrize( + "arg, flag", + [ + ("--int8", "INT8"), + ("--fp16", "FP16"), + ("--tf32", "TF32"), + ], + ) def test_precision_flags(self, trt_config_args, arg, flag): if flag == "TF32" and mod.version(trt.__version__) < mod.version("7.1"): pytest.skip("TF32 support was added in 7.1") @@ -57,6 +58,13 @@ class TestTrtConfigArgs(object): with builder, network, trt_config_args.create_config(builder, network=network) as config: assert config.get_flag(getattr(trt.BuilderFlag, flag)) + @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("8.0"), reason="SAFETY_SCOPE was added in TRT 8") + def test_restricted_flags(self, trt_config_args): + trt_config_args.parse_args(["--trt-safety-restricted"]) + builder, network = create_network() + + with builder, network, trt_config_args.create_config(builder, network=network) as config: + assert config.get_flag(getattr(trt.BuilderFlag, "SAFETY_SCOPE")) @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("8.0"), reason="Bugged before TRT 8") def test_tactic_replay(self, trt_config_args): @@ -69,12 +77,14 @@ class TestTrtConfigArgs(object): assert recorder.make_func == TacticRecorder assert recorder.path == f.name - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("8.0"), reason="Bugged before TRT 8") - @pytest.mark.parametrize("opt, cls", [ - ("--save-tactics", TacticRecorder), - ("--load-tactics", TacticReplayer), - ]) + @pytest.mark.parametrize( + "opt, cls", + [ + ("--save-tactics", TacticRecorder), + ("--load-tactics", TacticReplayer), + ], + ) def test_tactics(self, trt_config_args, opt, cls): with tempfile.NamedTemporaryFile("w+", suffix=".json") as f: if opt == "--load-tactics": @@ -87,24 +97,22 @@ class TestTrtConfigArgs(object): assert recorder.make_func == cls assert recorder.path == f.name - if mod.version(trt.__version__) < mod.version("8.0"): TACTIC_SOURCES_CASES = [ - ([], 3), # By default, all sources are enabled. + ([], 3), # By default, all sources are enabled. (["--tactic-sources"], 0), (["--tactic-sources", "CUBLAS"], 1), (["--tactic-sources", "CUBLAS_LT"], 2), - (["--tactic-sources", "CUblAS", "cublas_lt"], 3), # Not case sensitive - + (["--tactic-sources", "CUblAS", "cublas_lt"], 3), # Not case sensitive ] else: - TACTIC_SOURCES_CASES = [ - ([], 7), # By default, all sources are enabled. + TACTIC_SOURCES_CASES = [ + ([], 7), # By default, all sources are enabled. (["--tactic-sources"], 0), (["--tactic-sources", "CUBLAS"], 1), (["--tactic-sources", "CUBLAS_LT"], 2), (["--tactic-sources", "CUDNN"], 4), - (["--tactic-sources", "CUblAS", "cublas_lt"], 3), # Not case sensitive + (["--tactic-sources", "CUblAS", "cublas_lt"], 3), # Not case sensitive (["--tactic-sources", "CUBLAS", "cuDNN"], 5), (["--tactic-sources", "CUBLAS_LT", "CUDNN"], 6), (["--tactic-sources", "CUDNN", "cuBLAS", "CUBLAS_LT"], 7), @@ -118,7 +126,6 @@ class TestTrtConfigArgs(object): with builder, network, trt_config_args.create_config(builder, network=network) as config: assert config.get_tactic_sources() == expected - @pytest.mark.parametrize("base_class", ["IInt8LegacyCalibrator", "IInt8EntropyCalibrator2"]) def test_calibration_base_class(self, trt_config_args, base_class): trt_config_args.parse_args(["--int8", "--calibration-base-class", base_class]) @@ -128,13 +135,20 @@ class TestTrtConfigArgs(object): with builder, network, trt_config_args.create_config(builder, network=network) as config: assert isinstance(config.int8_calibrator, getattr(trt, base_class)) - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") def test_legacy_calibrator_params(self, trt_config_args): quantile = 0.25 regression_cutoff = 0.9 - trt_config_args.parse_args(["--int8", "--calibration-base-class=IInt8LegacyCalibrator", - "--quantile", str(quantile), "--regression-cutoff", str(regression_cutoff)]) + trt_config_args.parse_args( + [ + "--int8", + "--calibration-base-class=IInt8LegacyCalibrator", + "--quantile", + str(quantile), + "--regression-cutoff", + str(regression_cutoff), + ] + ) assert trt_config_args.quantile == quantile assert trt_config_args.regression_cutoff == regression_cutoff @@ -143,10 +157,16 @@ class TestTrtConfigArgs(object): assert config.int8_calibrator.get_quantile() == quantile assert config.int8_calibrator.get_regression_cutoff() == regression_cutoff - def test_no_deps_profiles_int8(self): arg_group = ArgGroupTestHelper(TrtConfigArgs()) - arg_group.parse_args(["--trt-min-shapes=input:[1,25,25]", "--trt-opt-shapes=input:[2,25,25]", "--trt-max-shapes=input:[4,25,25]", "--int8"]) + arg_group.parse_args( + [ + "--trt-min-shapes=input:[1,25,25]", + "--trt-opt-shapes=input:[2,25,25]", + "--trt-max-shapes=input:[4,25,25]", + "--int8", + ] + ) for (min_shapes, opt_shapes, max_shapes) in arg_group.profile_dicts: assert min_shapes["input"] == [1, 25, 25] @@ -162,12 +182,13 @@ class TestTrtConfigArgs(object): assert config.num_optimization_profiles == 1 assert config.get_flag(trt.BuilderFlag.INT8) - def test_config_script(self): arg_group = ArgGroupTestHelper(TrtConfigArgs()) with tempfile.NamedTemporaryFile("w+", suffix=".py") as f: - f.write(dedent(""" + f.write( + dedent( + """ from polygraphy.backend.trt import CreateConfig from polygraphy import func import tensorrt as trt @@ -175,7 +196,9 @@ class TestTrtConfigArgs(object): @func.extend(CreateConfig()) def my_load_config(config): config.set_flag(trt.BuilderFlag.FP16) - """)) + """ + ) + ) f.flush() arg_group.parse_args(["--trt-config-script", f.name, "--trt-config-func-name=my_load_config"]) @@ -187,18 +210,20 @@ class TestTrtConfigArgs(object): assert isinstance(config, trt.IBuilderConfig) assert config.get_flag(trt.BuilderFlag.FP16) - - @pytest.mark.parametrize("args", [ - ["--int8", "--calibration-base-class", "IInt8LegacyCalibrator'"], - ["--int8", "--calibration-base-class", "IInt8LegacyCalibrator\""], - ["--int8", "--calibration-base-class", "IInt8LegacyCalibrator)"], - ["--int8", "--calibration-base-class", "IInt8LegacyCalibrator}"], - ["--int8", "--calibration-base-class", "IInt8LegacyCalibrator]"], - ["--int8", "--calibration-base-class", "IInt8LegacyCalibrator));print(('hi'"], - ["--int8", "--calibration-base-class", "IInt8LegacyCalibrator;print(('hi')"], - ["--int8", "--calibration-base-class", "IInt8LegacyCalibrator';print('hi')"], - ["--tactic-sources", "CUBLAS, fp16=True"], - ]) + @pytest.mark.parametrize( + "args", + [ + ["--int8", "--calibration-base-class", "IInt8LegacyCalibrator'"], + ["--int8", "--calibration-base-class", 'IInt8LegacyCalibrator"'], + ["--int8", "--calibration-base-class", "IInt8LegacyCalibrator)"], + ["--int8", "--calibration-base-class", "IInt8LegacyCalibrator}"], + ["--int8", "--calibration-base-class", "IInt8LegacyCalibrator]"], + ["--int8", "--calibration-base-class", "IInt8LegacyCalibrator));print(('hi'"], + ["--int8", "--calibration-base-class", "IInt8LegacyCalibrator;print(('hi')"], + ["--int8", "--calibration-base-class", "IInt8LegacyCalibrator';print('hi')"], + ["--tactic-sources", "CUBLAS, fp16=True"], + ], + ) def test_code_injection_checks(self, trt_config_args, args): - with pytest.raises(SystemExit): + with pytest.raises(PolygraphyException): trt_config_args.parse_args(args) diff --git a/tools/Polygraphy/tests/tools/args/trt/test_loader.py b/tools/Polygraphy/tests/tools/args/trt/test_loader.py index c7fa1cc5..eef2b4ad 100644 --- a/tools/Polygraphy/tests/tools/args/trt/test_loader.py +++ b/tools/Polygraphy/tests/tools/args/trt/test_loader.py @@ -18,18 +18,24 @@ import tempfile import pytest import tensorrt as trt -from polygraphy.backend.trt import (engine_bytes_from_network, - network_from_onnx_path, create_network) -from polygraphy.tools.args import (ModelArgs, OnnxLoaderArgs, TrtConfigArgs, - TrtEngineLoaderArgs, TrtNetworkLoaderArgs, - TrtPluginLoaderArgs) +from polygraphy.backend.trt import engine_bytes_from_network, network_from_onnx_path, create_network +from polygraphy.tools.args import ( + ModelArgs, + OnnxLoaderArgs, + TrtConfigArgs, + TrtEngineLoaderArgs, + TrtNetworkLoaderArgs, + TrtPluginLoaderArgs, +) from tests.models.meta import ONNX_MODELS from tests.tools.args.helper import ArgGroupTestHelper class TestTrtNetworkLoaderArgs(object): def test_load_network(self): - arg_group = ArgGroupTestHelper(TrtNetworkLoaderArgs(), deps=[ModelArgs(), OnnxLoaderArgs(), TrtPluginLoaderArgs()]) + arg_group = ArgGroupTestHelper( + TrtNetworkLoaderArgs(), deps=[ModelArgs(), OnnxLoaderArgs(), TrtPluginLoaderArgs()] + ) arg_group.parse_args([ONNX_MODELS["identity_identity"].path, "--trt-outputs=identity_out_0"]) builder, network, parser = arg_group.load_network() @@ -40,8 +46,10 @@ class TestTrtNetworkLoaderArgs(object): @pytest.fixture() def engine_loader_args(): - return ArgGroupTestHelper(TrtEngineLoaderArgs(), deps=[ModelArgs(), OnnxLoaderArgs(), TrtConfigArgs(), - TrtPluginLoaderArgs(), TrtNetworkLoaderArgs()]) + return ArgGroupTestHelper( + TrtEngineLoaderArgs(), + deps=[ModelArgs(), OnnxLoaderArgs(), TrtConfigArgs(), TrtPluginLoaderArgs(), TrtNetworkLoaderArgs()], + ) class TestTrtEngineLoaderArgs(object): @@ -53,7 +61,6 @@ class TestTrtEngineLoaderArgs(object): assert len(engine) == 2 assert engine[1] == "identity_out_0" - def test_build_engine_custom_network(self, engine_loader_args): engine_loader_args.parse_args([]) @@ -69,9 +76,10 @@ class TestTrtEngineLoaderArgs(object): assert engine[0] == "input" assert engine[1] == "output" - def test_load_serialized_engine(self, engine_loader_args): - with tempfile.NamedTemporaryFile() as f, engine_bytes_from_network(network_from_onnx_path(ONNX_MODELS["identity"].path)) as engine_bytes: + with tempfile.NamedTemporaryFile() as f, engine_bytes_from_network( + network_from_onnx_path(ONNX_MODELS["identity"].path) + ) as engine_bytes: f.write(engine_bytes) f.flush() diff --git a/tools/Polygraphy/tests/tools/common.py b/tools/Polygraphy/tests/tools/common.py index 2ad0e7e4..b833295f 100644 --- a/tools/Polygraphy/tests/tools/common.py +++ b/tools/Polygraphy/tests/tools/common.py @@ -26,7 +26,7 @@ polygraphy = os.path.join(BIN_DIR, "polygraphy") def check_subprocess(status): if status.returncode: - G_LOGGER.exit(status.stdout + status.stderr) + G_LOGGER.critical(status.stdout + status.stderr) def run_polygraphy(additional_opts=[], *args, **kwargs): diff --git a/tools/Polygraphy/tests/tools/fake_reduce_checker.py b/tools/Polygraphy/tests/tools/fake_reduce_checker.py index 3c811a98..a6033cd0 100755 --- a/tools/Polygraphy/tests/tools/fake_reduce_checker.py +++ b/tools/Polygraphy/tests/tools/fake_reduce_checker.py @@ -24,13 +24,23 @@ import argparse import sys import onnx + def main(): parser = argparse.ArgumentParser(description="Makes Polygraphy think a node in a model is failing") parser.add_argument("model", help="The ONNX model") - parser.add_argument("--fail-node", help="The name(s) of the node(s) that 'fails'. " - "If multiple nodes are specified, they must all be present to cause a failure.", required=True, nargs="+") - parser.add_argument("--default-return-code", help="The return code to use when there are no failures. ", default=0, type=int) - parser.add_argument("--fail-return-code", help="The return code to use when there is a failure. ", default=1, type=int) + parser.add_argument( + "--fail-node", + help="The name(s) of the node(s) that 'fails'. " + "If multiple nodes are specified, they must all be present to cause a failure.", + required=True, + nargs="+", + ) + parser.add_argument( + "--default-return-code", help="The return code to use when there are no failures. ", default=0, type=int + ) + parser.add_argument( + "--fail-return-code", help="The return code to use when there is a failure. ", default=1, type=int + ) args = parser.parse_args() diff --git a/tools/Polygraphy/tests/tools/test_convert.py b/tools/Polygraphy/tests/tools/test_convert.py index 2f9dba50..93253408 100644 --- a/tools/Polygraphy/tests/tools/test_convert.py +++ b/tools/Polygraphy/tests/tools/test_convert.py @@ -32,10 +32,11 @@ class TestConvertToOnnx(object): run_polygraphy_convert([TF_MODELS["identity"].path, "--model-type=frozen", "-o", outmodel.name]) assert onnx.load(outmodel.name) - def test_fp_to_fp16(self): with tempfile.NamedTemporaryFile() as outmodel: - run_polygraphy_convert([ONNX_MODELS["identity_identity"].path, "--convert-to=onnx", "--fp-to-fp16", "-o", outmodel.name]) + run_polygraphy_convert( + [ONNX_MODELS["identity_identity"].path, "--convert-to=onnx", "--fp-to-fp16", "-o", outmodel.name] + ) assert onnx.load(outmodel.name).graph.value_info[0].type.tensor_type.elem_type == 10 @@ -45,22 +46,24 @@ class TestConvertToTrt(object): with loader() as engine: assert isinstance(engine, trt.ICudaEngine) - def test_onnx_to_trt(self): with tempfile.NamedTemporaryFile(suffix=".engine") as outmodel: run_polygraphy_convert([ONNX_MODELS["identity"].path, "--model-type=onnx", "-o", outmodel.name]) self.check_engine(outmodel.name) - - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("8.0"), reason="Bug in older versions of TRT breaks this test") + @pytest.mark.skipif( + mod.version(trt.__version__) < mod.version("8.0"), reason="Bug in older versions of TRT breaks this test" + ) def test_tf_to_onnx_to_trt(self): with tempfile.NamedTemporaryFile() as outmodel: - run_polygraphy_convert([TF_MODELS["identity"].path, "--model-type=frozen", "--convert-to=trt", "-o", outmodel.name]) + run_polygraphy_convert( + [TF_MODELS["identity"].path, "--model-type=frozen", "--convert-to=trt", "-o", outmodel.name] + ) self.check_engine(outmodel.name) - def test_trt_network_config_script_to_engine(self): - script = dedent(""" + script = dedent( + """ from polygraphy.backend.trt import CreateNetwork, CreateConfig from polygraphy import func import tensorrt as trt @@ -74,20 +77,44 @@ class TestConvertToTrt(object): @func.extend(CreateConfig()) def load_config(config): config.set_flag(trt.BuilderFlag.FP16) - """) + """ + ) with tempfile.NamedTemporaryFile("w+", suffix=".py") as f, tempfile.NamedTemporaryFile() as outmodel: f.write(script) f.flush() - run_polygraphy_convert([f.name, "--model-type=trt-network-script", "--trt-network-func-name=my_load_network", "--trt-config-script", f.name, - "--convert-to=trt", "-o", outmodel.name]) + run_polygraphy_convert( + [ + f.name, + "--model-type=trt-network-script", + "--trt-network-func-name=my_load_network", + "--trt-config-script", + f.name, + "--convert-to=trt", + "-o", + outmodel.name, + ] + ) self.check_engine(outmodel.name) - def test_modify_onnx_outputs(self): with tempfile.NamedTemporaryFile(suffix=".onnx") as outmodel: - run_polygraphy_convert([ONNX_MODELS["identity_identity"].path, "-o", outmodel.name, "--onnx-outputs", "mark", "all"]) + run_polygraphy_convert( + [ONNX_MODELS["identity_identity"].path, "-o", outmodel.name, "--onnx-outputs", "mark", "all"] + ) model = onnx.load(outmodel.name) assert len(model.graph.output) == 2 + + +class TestConvertToOnnxLikeTrt(object): + @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.2"), reason="Unsupported for TRT 7.1 and older") + @pytest.mark.parametrize( + "model_name", ["identity", "empty_tensor_expand", "const_foldable", "and", "scan", "dim_param", "tensor_attr"] + ) + def test_onnx_to_trt_to_onnx_like(self, model_name): + with tempfile.NamedTemporaryFile() as outmodel: + run_polygraphy_convert( + [ONNX_MODELS[model_name].path, "--convert-to=onnx-like-trt-network", "-o", outmodel.name] + ) diff --git a/tools/Polygraphy/tests/tools/test_debug.py b/tools/Polygraphy/tests/tools/test_debug.py index 96702895..8741d8b1 100644 --- a/tools/Polygraphy/tests/tools/test_debug.py +++ b/tools/Polygraphy/tests/tools/test_debug.py @@ -53,6 +53,7 @@ def replay_dir(request): return TacticReplayData().add("layer0", Algorithm.from_trt(fake_context("layer0"), fake_algo(0, tactic))) with tempfile.TemporaryDirectory() as dir: + def make_path(prefix, *args): path = os.path.join(dir, prefix) if request.param: @@ -68,28 +69,30 @@ def replay_dir(request): save_json(make_replay(1), make_path("bad", "0.json")) save_json(make_replay(2), make_path("bad", "1.json")) - EXPECTED_OUTPUT = dedent(""" + EXPECTED_OUTPUT = dedent( + """ [I] Loaded 2 good tactic replays. [I] Loaded 2 bad tactic replays. [I] Found potentially bad tactics: [I] Layer: layer0 Algorithms: ["(Implementation: 0, Tactic: 2) | Inputs: (('TensorFormat.LINEAR', 'DataType.FLOAT'),) | Outputs: (('TensorFormat.LINEAR', 'DataType.FLOAT'),)"] - """) + """ + ) yield dir, EXPECTED_OUTPUT class TestDiffTactics(object): def check_output(self, status, expected_output): - output = "\n".join(line for line in status.stdout.strip().splitlines() if "Loading tactic replay file from " not in line) + output = "\n".join( + line for line in status.stdout.strip().splitlines() if "Loading tactic replay file from " not in line + ) assert output == expected_output.strip() - def test_dir(self, replay_dir): replay_dir, expected_output = replay_dir status = run_polygraphy_debug(["diff-tactics", "--dir", replay_dir], disable_verbose=True) self.check_output(status, expected_output) - def test_good_bad(self, replay_dir): replay_dir, expected_output = replay_dir @@ -104,17 +107,39 @@ class TestBuild(object): def test_good_bad(self): with tempfile.TemporaryDirectory() as outdir: # Also includes --show-output sanity test - status = run_polygraphy_debug(["build", ONNX_MODELS["identity"].path, "--save-tactics=replay.json", "--show-output", - "--artifacts-dir", outdir, "--until=good", "--artifacts", "replay.json", - "--check", "true"], - cwd=outdir) + status = run_polygraphy_debug( + [ + "build", + ONNX_MODELS["identity"].path, + "--save-tactics=replay.json", + "--show-output", + "--artifacts-dir", + outdir, + "--until=good", + "--artifacts", + "replay.json", + "--check", + "true", + ], + cwd=outdir, + ) assert "Passed: 1/1 | Pass Rate: 100.0%" in status.stdout - - status = run_polygraphy_debug(["build", ONNX_MODELS["identity"].path, "--save-tactics=replay.json", - "--artifacts-dir", outdir, "--until=bad", "--artifacts", "replay.json", - "--check", "false"], - cwd=outdir) + status = run_polygraphy_debug( + [ + "build", + ONNX_MODELS["identity"].path, + "--save-tactics=replay.json", + "--artifacts-dir", + outdir, + "--until=bad", + "--artifacts", + "replay.json", + "--check", + "false", + ], + cwd=outdir, + ) assert "Passed: 0/1 | Pass Rate: 0.0%" in status.stdout def check_outdir(subdir): @@ -132,26 +157,60 @@ class TestPrecision(object): @pytest.mark.parametrize("check_status", ["true", "false"]) @pytest.mark.parametrize("mode", ["bisect", "linear"]) @pytest.mark.parametrize("direction", ["forward", "reverse"]) - def test_sanity(self, mode, direction, check_status): + @pytest.mark.parametrize("model", ["reducable", "const_foldable"]) + def test_sanity(self, mode, direction, check_status, model): with tempfile.TemporaryDirectory() as outdir: - run_polygraphy_debug(["precision", "--mode", mode, "--direction", direction, ONNX_MODELS["identity_identity"].path, "--int8", - "--check", check_status], - cwd=outdir) + run_polygraphy_debug( + [ + "precision", + "--mode", + mode, + "--direction", + direction, + ONNX_MODELS[model].path, + "--int8", + "--check", + check_status, + ], + cwd=outdir, + ) class TestReduce(object): FAKE_REDUCE_CHECKER = os.path.join(os.path.dirname(__file__), "fake_reduce_checker.py") # Test left branch, right branch, at the point of branching, and after the branch. - @pytest.mark.parametrize("fail_node", ["onnx_graphsurgeon_node_1", "onnx_graphsurgeon_node_3", "onnx_graphsurgeon_node_5", - "onnx_graphsurgeon_node_7", "onnx_graphsurgeon_node_9"]) + @pytest.mark.parametrize( + "fail_node", + [ + "onnx_graphsurgeon_node_1", + "onnx_graphsurgeon_node_3", + "onnx_graphsurgeon_node_5", + "onnx_graphsurgeon_node_7", + "onnx_graphsurgeon_node_9", + ], + ) @pytest.mark.parametrize("mode", ["linear", "bisect"]) def test_can_isolate_node(self, fail_node, mode): with tempfile.TemporaryDirectory() as outdir: - run_polygraphy_debug(["reduce", ONNX_MODELS["reducable"].path, "--output=reduced.onnx", "--mode", mode, "--show-output", - "--min-good=good_reduced.onnx", - "--check", TestReduce.FAKE_REDUCE_CHECKER, "polygraphy_debug.onnx", "--fail-node", fail_node], - disable_verbose=True, cwd=outdir) + run_polygraphy_debug( + [ + "reduce", + ONNX_MODELS["reducable"].path, + "--output=reduced.onnx", + "--mode", + mode, + "--show-output", + "--min-good=good_reduced.onnx", + "--check", + TestReduce.FAKE_REDUCE_CHECKER, + "polygraphy_debug.onnx", + "--fail-node", + fail_node, + ], + disable_verbose=True, + cwd=outdir, + ) model = onnx_from_path(os.path.join(outdir, "reduced.onnx")) @@ -177,14 +236,27 @@ class TestReduce(object): if good_model: assert model != good_model - # Run a test where the last node in the model is failing. # If we're not reducing inputs, then only the outputs should change def test_no_reduce_inputs(self): with tempfile.TemporaryDirectory() as outdir: - run_polygraphy_debug(["reduce", ONNX_MODELS["reducable"].path, "--output=reduced.onnx", "--show-output", "--no-reduce-inputs", "--mode=linear", - "--check", TestReduce.FAKE_REDUCE_CHECKER, "polygraphy_debug.onnx", "--fail-node", "onnx_graphsurgeon_node_7"], - disable_verbose=True, cwd=outdir) + run_polygraphy_debug( + [ + "reduce", + ONNX_MODELS["reducable"].path, + "--output=reduced.onnx", + "--show-output", + "--no-reduce-inputs", + "--mode=linear", + "--check", + TestReduce.FAKE_REDUCE_CHECKER, + "polygraphy_debug.onnx", + "--fail-node", + "onnx_graphsurgeon_node_7", + ], + disable_verbose=True, + cwd=outdir, + ) model = onnx_from_path(os.path.join(outdir, "reduced.onnx")) assert len(model.graph.node) == 4 @@ -195,14 +267,27 @@ class TestReduce(object): node_names = [node.name for node in model.graph.node] assert "onnx_graphsurgeon_node_7" in node_names - # Run a test where an input node in the model is failing. # If we're not reducing outputs, then only the inputs should change def test_no_reduce_outputs(self): with tempfile.TemporaryDirectory() as outdir: - run_polygraphy_debug(["reduce", ONNX_MODELS["reducable"].path, "--output=reduced.onnx", "--show-output", "--no-reduce-outputs", "--mode=linear", - "--check", TestReduce.FAKE_REDUCE_CHECKER, "polygraphy_debug.onnx", "--fail-node", "onnx_graphsurgeon_node_3"], - disable_verbose=True, cwd=outdir) + run_polygraphy_debug( + [ + "reduce", + ONNX_MODELS["reducable"].path, + "--output=reduced.onnx", + "--show-output", + "--no-reduce-outputs", + "--mode=linear", + "--check", + TestReduce.FAKE_REDUCE_CHECKER, + "polygraphy_debug.onnx", + "--fail-node", + "onnx_graphsurgeon_node_3", + ], + disable_verbose=True, + cwd=outdir, + ) model = onnx_from_path(os.path.join(outdir, "reduced.onnx")) assert len(model.graph.node) == 4 @@ -213,68 +298,119 @@ class TestReduce(object): node_names = [node.name for node in model.graph.node] assert "onnx_graphsurgeon_node_7" in node_names - # In this test, we set up the checker to return 1 for the bad node, but 2 in other cases. # We want to ignore '2's and treat them as successes def test_reduce_custom_return_code(self): with tempfile.TemporaryDirectory() as outdir: - run_polygraphy_debug(["reduce", ONNX_MODELS["reducable"].path, "--output=reduced.onnx", "--show-output", "--fail-code=1", # Only 1s are real failures. - "--check", TestReduce.FAKE_REDUCE_CHECKER, "polygraphy_debug.onnx", "--fail-node", "onnx_graphsurgeon_node_5", "--default-return-code=2"], - disable_verbose=True, cwd=outdir) + run_polygraphy_debug( + [ + "reduce", + ONNX_MODELS["reducable"].path, + "--output=reduced.onnx", + "--show-output", + "--fail-code=1", # Only 1s are real failures. + "--check", + TestReduce.FAKE_REDUCE_CHECKER, + "polygraphy_debug.onnx", + "--fail-node", + "onnx_graphsurgeon_node_5", + "--default-return-code=2", + ], + disable_verbose=True, + cwd=outdir, + ) model = onnx_from_path(os.path.join(outdir, "reduced.onnx")) assert len(model.graph.node) == 1 assert model.graph.node[0].name == "onnx_graphsurgeon_node_5" - # Here we set the failure return code to 0, which would normally mark succeeding cases as failing. # However, since we also set the --fail-regex, it will only regard as failures those runs which print the error message. - @pytest.mark.parametrize("fail_code_arg", [ - [], - ["--fail-code=0"], - ]) + @pytest.mark.parametrize( + "fail_code_arg", + [ + [], + ["--fail-code=0"], + ], + ) def test_reduce_custom_fail_message(self, fail_code_arg): with tempfile.TemporaryDirectory() as outdir: # fake_reduce_checker will alternate error messages based on whether an arbitrary node is present in the model. - run_polygraphy_debug(["reduce", ONNX_MODELS["reducable"].path, "--output=reduced.onnx", "--show-output", "--fail-regex", "REALLY BAD", "BAD NODE"] - + fail_code_arg - + ["--check", TestReduce.FAKE_REDUCE_CHECKER, "polygraphy_debug.onnx", "--fail-node", "onnx_graphsurgeon_node_5", "--fail-return-code=0"], - disable_verbose=True, cwd=outdir) + run_polygraphy_debug( + [ + "reduce", + ONNX_MODELS["reducable"].path, + "--output=reduced.onnx", + "--show-output", + "--fail-regex", + "REALLY BAD", + "BAD NODE", + ] + + fail_code_arg + + [ + "--check", + TestReduce.FAKE_REDUCE_CHECKER, + "polygraphy_debug.onnx", + "--fail-node", + "onnx_graphsurgeon_node_5", + "--fail-return-code=0", + ], + disable_verbose=True, + cwd=outdir, + ) model = onnx_from_path(os.path.join(outdir, "reduced.onnx")) assert len(model.graph.node) == 1 assert model.graph.node[0].name == "onnx_graphsurgeon_node_5" - # In cases where both sides of a branch are required to reproduce the failure, # reduce should not remove the branch. - @pytest.mark.parametrize("fail_nodes", [ - ["onnx_graphsurgeon_node_1", "onnx_graphsurgeon_node_3"], - ["onnx_graphsurgeon_node_7", "onnx_graphsurgeon_node_9"] - ]) + @pytest.mark.parametrize( + "fail_nodes", + [ + ["onnx_graphsurgeon_node_1", "onnx_graphsurgeon_node_3"], + ["onnx_graphsurgeon_node_7", "onnx_graphsurgeon_node_9"], + ], + ) def test_no_reduce_required_branches(self, fail_nodes): with tempfile.TemporaryDirectory() as outdir: - run_polygraphy_debug(["reduce", ONNX_MODELS["reducable"].path, "--output=reduced.onnx", "--show-output", - "--check", TestReduce.FAKE_REDUCE_CHECKER, "polygraphy_debug.onnx", - "--fail-node"] + fail_nodes, - disable_verbose=True, cwd=outdir) + run_polygraphy_debug( + [ + "reduce", + ONNX_MODELS["reducable"].path, + "--output=reduced.onnx", + "--show-output", + "--check", + TestReduce.FAKE_REDUCE_CHECKER, + "polygraphy_debug.onnx", + "--fail-node", + ] + + fail_nodes, + disable_verbose=True, + cwd=outdir, + ) model = onnx_from_path(os.path.join(outdir, "reduced.onnx")) node_names = [node.name for node in model.graph.node] assert all(fail_node in node_names for fail_node in fail_nodes) - assert len(model.graph.node) <= 3 # The branch on the opposite side of the model should be removed. + assert len(model.graph.node) <= 3 # The branch on the opposite side of the model should be removed. - - @pytest.mark.parametrize("opts", [ - [], - ["--force-fallback-shape-inference"] - ]) + @pytest.mark.parametrize("opts", [[], ["--force-fallback-shape-inference"]]) def test_reduce_shape_inference(self, opts): with tempfile.TemporaryDirectory() as outdir: - status = run_polygraphy_debug(["reduce", ONNX_MODELS["dynamic_identity"].path, "--output=reduced.onnx", - "--show-output", "--model-input-shapes=X:[1,2,5,5]"] + opts - + ["--check", "false"], - disable_verbose=True, cwd=outdir) + status = run_polygraphy_debug( + [ + "reduce", + ONNX_MODELS["dynamic_identity"].path, + "--output=reduced.onnx", + "--show-output", + "--model-input-shapes=X:[1,2,5,5]", + ] + + opts + + ["--check", "false"], + disable_verbose=True, + cwd=outdir, + ) model = onnx_from_path(os.path.join(outdir, "reduced.onnx")) graph = gs.import_onnx(model) assert tuple(graph.inputs[0].shape) == (1, 2, 5, 5) @@ -282,16 +418,18 @@ class TestReduce(object): class TestRepeat(object): - @pytest.mark.parametrize("until, check, expected_iters", [ - ("good", "true", 1), - ("bad", "false", 1), - ("5", "false", 5), - ]) + @pytest.mark.parametrize( + "until, check, expected_iters", + [ + ("good", "true", 1), + ("bad", "false", 1), + ("5", "false", 5), + ], + ) def test_until(self, until, check, expected_iters): status = run_polygraphy_debug(["repeat", "--until", until, "--check", check]) assert "Finished {:} iteration(s)".format(expected_iters) in status.stdout - def test_iteration_info(self): with tempfile.TemporaryDirectory() as outdir: iter_info = os.path.join(outdir, "iter_info.json") @@ -310,15 +448,39 @@ class TestRepeat(object): assert not os.path.exists(path) with open(path, "w") as f: f.write("File") - """.format(iter_info) + """.format( + iter_info + ) with open(check_script, "w") as f: f.write(dedent(check_num)) - status = run_polygraphy_debug(["repeat", "--until=5", "--iteration-info", iter_info, "--show-output", - "--check", sys.executable, check_script], cwd=outdir) + status = run_polygraphy_debug( + [ + "repeat", + "--until=5", + "--iteration-info", + iter_info, + "--show-output", + "--check", + sys.executable, + check_script, + ], + cwd=outdir, + ) assert "FAILED" not in status.stdout assert "Passed: 5/5 | Pass Rate: 100.0%" in status.stdout # Iteration info should be cleaned up afterwards assert not os.path.exists(iter_info) + + def test_ignore_fail_code(self): + # Sanity check to make sure the command normally fails. + status = run_polygraphy_debug(["repeat", "--until=5", "--check", "false"]) + assert "Passed: 0/5 | Pass Rate: 0.0%" in status.stdout + + status = run_polygraphy_debug(["repeat", "--until=5", "--ignore-fail-code=2", "--check", "false"]) + assert "Passed: 0/5 | Pass Rate: 0.0%" in status.stdout + + status = run_polygraphy_debug(["repeat", "--until=5", "--ignore-fail-code=1", "--check", "false"]) + assert "Passed: 5/5 | Pass Rate: 100.0%" in status.stdout diff --git a/tools/Polygraphy/tests/tools/test_inspect.py b/tools/Polygraphy/tests/tools/test_inspect.py index a9bfd3ee..4f3399d9 100644 --- a/tools/Polygraphy/tests/tools/test_inspect.py +++ b/tools/Polygraphy/tests/tools/test_inspect.py @@ -25,7 +25,9 @@ from tests.tools.common import run_polygraphy_inspect, run_polygraphy_run @pytest.fixture(scope="session", params=["none", "basic", "attrs", "full"]) def run_inspect_model(request): - yield lambda additional_opts: run_polygraphy_inspect(["model"] + ["--mode={:}".format(request.param)] + additional_opts) + yield lambda additional_opts: run_polygraphy_inspect( + ["model"] + ["--mode={:}".format(request.param)] + additional_opts + ) @pytest.fixture(scope="session") @@ -35,7 +37,7 @@ def identity_engine(): yield outpath.name -def check_lines_match(actual, expected): +def check_lines_match(actual, expected, should_check_line=lambda x: True): print("Actual output:\n{:}".format(actual)) actual = [line for line in actual.splitlines() if "Loading" not in line] @@ -47,12 +49,15 @@ def check_lines_match(actual, expected): exline = exline.rstrip() print("Checking line : {:}".format(acline)) print("Expecting line: {:}".format(exline)) - assert acline == exline + if should_check_line(exline): + assert acline == exline # ONNX cases ONNX_CASES = [ - ["identity", "none", + [ + "identity", + "none", r""" [I] ==== ONNX Model ==== Name: test_identity | Opset: 8 @@ -66,9 +71,11 @@ ONNX_CASES = [ ---- 0 Initializer(s) ---- ---- 1 Node(s) ---- - """ + """, ], - ["identity", "basic", + [ + "identity", + "basic", r""" [I] ==== ONNX Model ==== Name: test_identity | Opset: 8 @@ -86,9 +93,11 @@ ONNX_CASES = [ Node 0 | [Op: Identity] {x [dtype=float32, shape=(1, 1, 2, 2)]} -> {y [dtype=float32, shape=(1, 1, 2, 2)]} - """ + """, ], - ["identity_with_initializer", "basic", + [ + "identity_with_initializer", + "basic", r""" [I] ==== ONNX Model ==== Name: onnx_graphsurgeon | Opset: 11 @@ -106,9 +115,11 @@ ONNX_CASES = [ Node 0 | [Op: Identity] {Initializer | X [dtype=float32, shape=(2, 2)]} -> {Y [dtype=float32, shape=(2, 2)]} - """ + """, ], - ["identity_with_initializer", "full", + [ + "identity_with_initializer", + "full", r""" [I] ==== ONNX Model ==== Name: onnx_graphsurgeon | Opset: 11 @@ -128,9 +139,11 @@ ONNX_CASES = [ Node 0 | [Op: Identity] {Initializer | X [dtype=float32, shape=(2, 2)]} -> {Y [dtype=float32, shape=(2, 2)]} - """ + """, ], - ["tensor_attr", "basic", + [ + "tensor_attr", + "basic", r""" [I] ==== ONNX Model ==== Name: onnx_graphsurgeon | Opset: 11 @@ -147,9 +160,11 @@ ONNX_CASES = [ ---- 1 Node(s) ---- Node 0 | [Op: Constant] {} -> {const_out [dtype=float32, shape=(14, 14)]} - """ + """, ], - ["tensor_attr", "attrs", + [ + "tensor_attr", + "attrs", r""" [I] ==== ONNX Model ==== Name: onnx_graphsurgeon | Opset: 11 @@ -168,9 +183,11 @@ ONNX_CASES = [ {} -> {const_out [dtype=float32, shape=(14, 14)]} ---- Attributes ---- value = Tensor: [dtype=float32, shape=[14, 14]] - """ + """, ], - ["tensor_attr", "full", + [ + "tensor_attr", + "full", r""" [I] ==== ONNX Model ==== Name: onnx_graphsurgeon | Opset: 11 @@ -203,9 +220,11 @@ ONNX_CASES = [ [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]] - """ + """, ], - ["scan", "full", + [ + "scan", + "full", r""" [I] ==== ONNX Model ==== Name: graph | Opset: 10 @@ -251,9 +270,11 @@ ONNX_CASES = [ -> {scan_out [dtype=float32, shape=(2,)]} num_scan_inputs = 1 - """ + """, ], - ["dim_param", "basic", + [ + "dim_param", + "basic", r""" [I] ==== ONNX Model ==== Name: tf2onnx | Opset: 10 @@ -271,7 +292,7 @@ ONNX_CASES = [ Node 0 | [Op: Identity] {Input:0 [dtype=float32, shape=('dim0', 16, 128)]} -> {Output:0 [dtype=float32, shape=('dim0', 16, 128)]} - """ + """, ], ] @@ -280,14 +301,15 @@ class TestInspectModel(object): @pytest.mark.parametrize("case", ONNX_CASES, ids=lambda case: "{:}-{:}".format(case[0], case[1])) def test_model_onnx(self, case): model, mode, expected = case - status = run_polygraphy_inspect(["model", ONNX_MODELS[model].path, "--mode={:}".format(mode)], disable_verbose=True) + status = run_polygraphy_inspect( + ["model", ONNX_MODELS[model].path, "--mode={:}".format(mode)], disable_verbose=True + ) expected = dedent(expected).strip() - actual = "\n".join(status.stdout.splitlines()[1:]) # Ignore loading message + actual = "\n".join(status.stdout.splitlines()[1:]) # Ignore loading message check_lines_match(actual, expected) - @pytest.mark.parametrize("model", ["identity", "scan", "tensor_attr"]) def test_model_trt_sanity(self, run_inspect_model, model): import tensorrt as trt @@ -300,9 +322,9 @@ class TestInspectModel(object): run_inspect_model([ONNX_MODELS[model].path, "--display-as=trt"]) - def test_model_trt_network_script(self): - script = dedent(""" + script = dedent( + """ from polygraphy.backend.trt import CreateNetwork from polygraphy import func import tensorrt as trt @@ -312,7 +334,8 @@ class TestInspectModel(object): inp = network.add_input("input", dtype=trt.float32, shape=(1, 1)) out = network.add_identity(inp).get_output(0) network.mark_output(out) - """) + """ + ) with tempfile.NamedTemporaryFile("w+", suffix=".py") as f: f.write(script) @@ -320,11 +343,9 @@ class TestInspectModel(object): run_polygraphy_inspect(["model", f.name]) - def test_model_trt_engine_sanity(self, run_inspect_model, identity_engine): run_inspect_model([identity_engine, "--model-type=engine"]) - def test_model_tf_sanity(self, run_inspect_model): run_inspect_model([TF_MODELS["identity"].path, "--model-type=frozen"]) @@ -336,7 +357,6 @@ class TestInspectData(object): run_polygraphy_run([ONNX_MODELS["identity"].path, "--onnxrt", "--save-outputs", outpath.name]) run_polygraphy_inspect(["data", outpath.name] + opts) - @pytest.mark.parametrize("opts", [[], ["--show-values"]]) def test_inputs(self, opts): with tempfile.NamedTemporaryFile() as outpath: @@ -345,14 +365,16 @@ class TestInspectData(object): TACTIC_REPLAY_CASES = [ - ["identity", + [ + "identity", r""" [I] Layer: node_of_y Algorithm: (Implementation: -2147483642, Tactic: 0) | Inputs: (('TensorFormat.LINEAR', 'DataType.FLOAT'),) | Outputs: (('TensorFormat.LINEAR', 'DataType.FLOAT'),) - """ + """, ], ] + @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("8.0"), reason="Unsupported for TRT 7.2 and older") class TestInspectTactics(object): @pytest.mark.parametrize("case", TACTIC_REPLAY_CASES, ids=lambda case: case[0]) @@ -366,4 +388,4 @@ class TestInspectTactics(object): expected = dedent(expected).strip() actual = status.stdout - check_lines_match(actual, expected) + check_lines_match(actual, expected, should_check_line=lambda line: "Algorithm: " not in line) diff --git a/tools/Polygraphy/tests/tools/test_polygraphy.py b/tools/Polygraphy/tests/tools/test_polygraphy.py index e93c7b86..f2282d3b 100644 --- a/tools/Polygraphy/tests/tools/test_polygraphy.py +++ b/tools/Polygraphy/tests/tools/test_polygraphy.py @@ -7,4 +7,8 @@ from tests.tools.common import run_polygraphy class TestPolygraphyBin(object): def test_version(self): status = run_polygraphy(["-v"]) - assert status.stdout.strip().replace("\n", " ").replace(" ", " ") == "Polygraphy | Version: {:} | Path: {:}".format(polygraphy.__version__, list(map(os.path.realpath, polygraphy.__path__))) + assert status.stdout.strip().replace("\n", " ").replace( + " ", " " + ) == "Polygraphy | Version: {:} | Path: {:}".format( + polygraphy.__version__, list(map(os.path.realpath, polygraphy.__path__)) + ) diff --git a/tools/Polygraphy/tests/tools/test_run.py b/tools/Polygraphy/tests/tools/test_run.py index d6b381f0..85da5194 100644 --- a/tools/Polygraphy/tests/tools/test_run.py +++ b/tools/Polygraphy/tests/tools/test_run.py @@ -19,12 +19,14 @@ import os import subprocess as sp import sys import tempfile +from textwrap import dedent +import onnx import pytest import tensorrt as trt from polygraphy import mod from polygraphy.json import load_json -from tests.helper import check_file_non_empty, get_file_size +from tests.helper import get_file_size, is_file_non_empty from tests.models.meta import ONNX_MODELS, TF_MODELS from tests.tools.common import ROOT_DIR, check_subprocess, run_polygraphy_run @@ -44,11 +46,13 @@ class TestLogging(object): def test_logger_verbosity(self): run_polygraphy_run(["--silent"]) - - @pytest.mark.parametrize("log_path", [ - os.path.join("example", "example.log"), - "example.log", - ]) + @pytest.mark.parametrize( + "log_path", + [ + os.path.join("example", "example.log"), + "example.log", + ], + ) def test_log_file(self, log_path): with tempfile.TemporaryDirectory() as outdir: run_polygraphy_run(["--log-file", log_path], cwd=outdir) @@ -59,7 +63,6 @@ class TestTrtLegacy(object): def test_uff(self): run_polygraphy_run([TF_MODELS["identity"].path, "--trt-legacy"]) - @pytest.mark.skipif(mod.version(trt.__version__) >= mod.version("7.0"), reason="Unsupported in TRT 7.0 and later") def test_onnx(self): run_polygraphy_run([ONNX_MODELS["identity"].path, "--trt-legacy"]) @@ -69,83 +72,145 @@ class TestTrt(object): def test_basic(self): run_polygraphy_run([ONNX_MODELS["identity"].path, "--trt"]) - def test_plugins(self): run_polygraphy_run([ONNX_MODELS["identity"].path, "--trt", "--plugins", "libnvinfer_plugin.so"]) - def test_custom_outputs(self): run_polygraphy_run([ONNX_MODELS["identity_identity"].path, "--trt", "--trt-outputs", "identity_out_0"]) - def test_layerwise_outputs(self): with tempfile.NamedTemporaryFile() as outfile0: - run_polygraphy_run([ONNX_MODELS["identity_identity"].path, "--trt", "--trt-outputs", "mark", "all", "--save-outputs", outfile0.name]) + run_polygraphy_run( + [ + ONNX_MODELS["identity_identity"].path, + "--trt", + "--trt-outputs", + "mark", + "all", + "--save-outputs", + outfile0.name, + ] + ) results = load_json(outfile0.name) [result] = list(results.values())[0] assert len(result) == 2 assert "identity_out_0" in result assert "identity_out_2" in result - def test_exclude_outputs_with_layerwise(self): with tempfile.NamedTemporaryFile() as outfile0: - run_polygraphy_run([ONNX_MODELS["identity_identity"].path, "--trt", "--trt-outputs", "mark", "all", - "--trt-exclude-outputs", "identity_out_2", "--save-outputs", outfile0.name]) + run_polygraphy_run( + [ + ONNX_MODELS["identity_identity"].path, + "--trt", + "--trt-outputs", + "mark", + "all", + "--trt-exclude-outputs", + "identity_out_2", + "--save-outputs", + outfile0.name, + ] + ) results = load_json(outfile0.name) [result] = list(results.values())[0] assert len(result) == 1 assert "identity_out_0" in result - def test_int8(self): run_polygraphy_run([ONNX_MODELS["identity"].path, "--trt", "--int8"]) - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("8.0"), reason="API was added after TRT 7.2") def test_sparse_weights(self): run_polygraphy_run([ONNX_MODELS["identity"].path, "--trt", "--sparse-weights"]) - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") def test_input_shape(self): run_polygraphy_run([ONNX_MODELS["dynamic_identity"].path, "--trt", "--onnxrt", "--input-shapes", "X:[1,2,4,4]"]) - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") def test_dynamic_input_shape(self): - run_polygraphy_run([ONNX_MODELS["dynamic_identity"].path, "--trt", "--onnxrt", "--input-shapes", "X:[1,2,-1,4]"]) - + run_polygraphy_run( + [ONNX_MODELS["dynamic_identity"].path, "--trt", "--onnxrt", "--input-shapes", "X:[1,2,-1,4]"] + ) @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") def test_dynamic_input_shape(self): run_polygraphy_run([ONNX_MODELS["dynamic_identity"].path, "--trt", "--onnxrt", "--input-shapes", "X,1x2x-1x4"]) - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") def test_explicit_profile(self): - run_polygraphy_run([ONNX_MODELS["dynamic_identity"].path, "--trt", "--onnxrt", "--input-shapes", "X:[1,2,1,1]", - "--trt-min-shapes", "X:[1,2,1,1]", "--trt-opt-shapes", "X:[1,2,1,1]", "--trt-max-shapes", "X:[1,2,1,1]"]) - + run_polygraphy_run( + [ + ONNX_MODELS["dynamic_identity"].path, + "--trt", + "--onnxrt", + "--input-shapes", + "X:[1,2,1,1]", + "--trt-min-shapes", + "X:[1,2,1,1]", + "--trt-opt-shapes", + "X:[1,2,1,1]", + "--trt-max-shapes", + "X:[1,2,1,1]", + ] + ) @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") def test_explicit_profile_implicit_runtime_shape(self): - run_polygraphy_run([ONNX_MODELS["dynamic_identity"].path, "--trt", "--onnxrt", - "--trt-min-shapes", "X:[1,2,1,1]", "--trt-opt-shapes", "X:[1,2,1,1]", "--trt-max-shapes", "X:[1,2,1,1]"]) - + run_polygraphy_run( + [ + ONNX_MODELS["dynamic_identity"].path, + "--trt", + "--onnxrt", + "--trt-min-shapes", + "X:[1,2,1,1]", + "--trt-opt-shapes", + "X:[1,2,1,1]", + "--trt-max-shapes", + "X:[1,2,1,1]", + ] + ) @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") def test_explicit_profile_opt_runtime_shapes_differ(self): - run_polygraphy_run([ONNX_MODELS["dynamic_identity"].path, "--trt", "--onnxrt", "--input-shapes", "X:[1,2,2,2]", - "--trt-min-shapes", "X:[1,2,1,1]", "--trt-opt-shapes", "X:[1,2,3,3]", "--trt-max-shapes", "X:[1,2,4,4]"]) - + run_polygraphy_run( + [ + ONNX_MODELS["dynamic_identity"].path, + "--trt", + "--onnxrt", + "--input-shapes", + "X:[1,2,2,2]", + "--trt-min-shapes", + "X:[1,2,1,1]", + "--trt-opt-shapes", + "X:[1,2,3,3]", + "--trt-max-shapes", + "X:[1,2,4,4]", + ] + ) @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") def test_multiple_profiles(self): - run_polygraphy_run([ONNX_MODELS["dynamic_identity"].path, "--trt", "--onnxrt", - "--trt-min-shapes", "X:[1,2,1,1]", "--trt-opt-shapes", "X:[1,2,1,1]", "--trt-max-shapes", "X:[1,2,1,1]", - "--trt-min-shapes", "X:[1,2,4,4]", "--trt-opt-shapes", "X:[1,2,4,4]", "--trt-max-shapes", "X:[1,2,4,4]"]) - + run_polygraphy_run( + [ + ONNX_MODELS["dynamic_identity"].path, + "--trt", + "--onnxrt", + "--trt-min-shapes", + "X:[1,2,1,1]", + "--trt-opt-shapes", + "X:[1,2,1,1]", + "--trt-max-shapes", + "X:[1,2,1,1]", + "--trt-min-shapes", + "X:[1,2,4,4]", + "--trt-opt-shapes", + "X:[1,2,4,4]", + "--trt-max-shapes", + "X:[1,2,4,4]", + ] + ) def test_int8_calibration_cache(self): with tempfile.NamedTemporaryFile() as outpath: @@ -153,8 +218,7 @@ class TestTrt(object): if mod.version(trt.__version__) >= mod.version("7.0"): cmd += ["--onnxrt"] run_polygraphy_run(cmd) - check_file_non_empty(outpath.name) - + assert is_file_non_empty(outpath.name) @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") @pytest.mark.parametrize("base_class", ["IInt8LegacyCalibrator", "IInt8EntropyCalibrator2"]) @@ -164,7 +228,6 @@ class TestTrt(object): cmd += ["--onnxrt"] run_polygraphy_run() - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("8.0"), reason="Unsupported for TRT 7.2 and older") def test_timing_cache(self): with tempfile.TemporaryDirectory() as dir: @@ -173,7 +236,7 @@ class TestTrt(object): identity_cache = os.path.join(dir, "identity.cache") run_polygraphy_run([ONNX_MODELS["const_foldable"].path, "--trt", "--timing-cache", total_cache]) - check_file_non_empty(total_cache) + assert is_file_non_empty(total_cache) const_foldable_cache_size = get_file_size(total_cache) run_polygraphy_run([ONNX_MODELS["identity"].path, "--trt", "--timing-cache", identity_cache]) @@ -188,53 +251,69 @@ class TestTrt(object): # header information should not be duplicated. assert total_cache_size <= (const_foldable_cache_size + identity_cache_size) - def test_save_load_engine(self): with tempfile.NamedTemporaryFile() as outpath: run_polygraphy_run([ONNX_MODELS["identity"].path, "--trt", "--save-engine", outpath.name]) - check_file_non_empty(outpath.name) + assert is_file_non_empty(outpath.name) run_polygraphy_run(["--trt", outpath.name, "--model-type=engine"]) - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("8.0"), reason="Unsupported for TRT 7.2 and older") def test_tactic_replay(self): with tempfile.NamedTemporaryFile() as tactic_replay: run_polygraphy_run([ONNX_MODELS["identity"].path, "--trt", "--save-tactics", tactic_replay.name]) - check_file_non_empty(tactic_replay.name) + assert is_file_non_empty(tactic_replay.name) run_polygraphy_run([ONNX_MODELS["identity"].path, "--trt", "--load-tactics", tactic_replay.name]) - @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.2"), reason="Unsupported before TRT 7.2") def test_tactic_sources(self): run_polygraphy_run([ONNX_MODELS["identity"].path, "--trt", "--tactic-sources", "CUBLAS", "CUBLAS_LT"]) + def test_data_loader_script_calibration(self): + with tempfile.NamedTemporaryFile("w+", suffix=".py") as f: + f.write( + dedent( + """ + import numpy as np + + def load_data(): + for _ in range(5): + yield {"x": np.ones((1, 1, 2, 2), dtype=np.float32) * 6.4341} + """ + ) + ) + f.flush() + + run_polygraphy_run([ONNX_MODELS["identity"].path, "--trt", "--int8", "--data-loader-script", f.name]) + class TestTf(object): def test_tf(self): run_polygraphy_run([TF_MODELS["identity"].path, "--tf", "--gpu-memory-fraction=0.5"]) - def test_tf_save_pb(self): with tempfile.NamedTemporaryFile() as outpath: - run_polygraphy_run([TF_MODELS["identity"].path, "--tf", "--gpu-memory-fraction=0.5", "--save-pb", outpath.name]) - check_file_non_empty(outpath.name) - + run_polygraphy_run( + [TF_MODELS["identity"].path, "--tf", "--gpu-memory-fraction=0.5", "--save-pb", outpath.name] + ) + assert is_file_non_empty(outpath.name) def test_tf_save_tensorboard(self): with tempfile.TemporaryDirectory() as outdir: - run_polygraphy_run([TF_MODELS["identity"].path, "--tf", "--gpu-memory-fraction=0.5", "--save-tensorboard", outdir]) + run_polygraphy_run( + [TF_MODELS["identity"].path, "--tf", "--gpu-memory-fraction=0.5", "--save-tensorboard", outdir] + ) files = glob.glob("{:}{:}*".format(outdir, os.path.sep)) assert len(files) == 1 - @pytest.mark.skip(reason="Non-trivial to set up - requires CUPTI") def test_tf_save_timeline(self): with tempfile.NamedTemporaryFile() as outpath: - run_polygraphy_run([TF_MODELS["identity"].path, "--tf", "--gpu-memory-fraction=0.5", "--save-timeline", outpath.name]) + run_polygraphy_run( + [TF_MODELS["identity"].path, "--tf", "--gpu-memory-fraction=0.5", "--save-timeline", outpath.name] + ) timelines = glob.glob(os.path.join(outpath.name, "*")) for timeline in timelines: - check_file_non_empty(timeline) - + assert is_file_non_empty(timeline) @pytest.mark.skip(reason="Non-trivial to set up") def test_tftrt(self): @@ -245,45 +324,68 @@ class TestOnnxrt(object): def test_tf2onnxrt(self): run_polygraphy_run([TF_MODELS["identity"].path, "--onnxrt", "--model-type=frozen"]) - def test_tf2onnx_save_onnx(self): with tempfile.NamedTemporaryFile() as outpath: - run_polygraphy_run([TF_MODELS["identity"].path, "--onnxrt", "--model-type=frozen", "--save-onnx", outpath.name]) - check_file_non_empty(outpath.name) - import onnx + run_polygraphy_run( + [TF_MODELS["identity"].path, "--onnxrt", "--model-type=frozen", "--save-onnx", outpath.name] + ) + assert is_file_non_empty(outpath.name) assert onnx.load(outpath.name) - def test_onnx_rt(self): run_polygraphy_run([ONNX_MODELS["identity"].path, "--onnxrt"]) + def test_onnx_rt_save_onnx(self): + with tempfile.NamedTemporaryFile() as outpath: + run_polygraphy_run([ONNX_MODELS["identity"].path, "--onnxrt", "--save-onnx", outpath.name]) + assert is_file_non_empty(outpath.name) + assert onnx.load(outpath.name) def test_onnx_rt_custom_outputs(self): run_polygraphy_run([ONNX_MODELS["identity_identity"].path, "--onnxrt", "--onnx-outputs", "identity_out_0"]) - def test_onnx_rt_layerwise_outputs(self): with tempfile.NamedTemporaryFile() as outfile0: - run_polygraphy_run([ONNX_MODELS["identity_identity"].path, "--onnxrt", "--onnx-outputs", "mark", "all", "--save-outputs", outfile0.name]) + run_polygraphy_run( + [ + ONNX_MODELS["identity_identity"].path, + "--onnxrt", + "--onnx-outputs", + "mark", + "all", + "--save-outputs", + outfile0.name, + ] + ) results = load_json(outfile0.name) [result] = list(results.values())[0] assert len(result) == 2 assert "identity_out_0" in result assert "identity_out_2" in result - def test_onnx_rt_exclude_outputs_with_layerwise(self): with tempfile.NamedTemporaryFile() as outfile0: - run_polygraphy_run([ONNX_MODELS["identity_identity"].path, "--onnxrt", "--onnx-outputs", "mark", "all", "--onnx-exclude-outputs", "identity_out_2", "--save-outputs", outfile0.name]) + run_polygraphy_run( + [ + ONNX_MODELS["identity_identity"].path, + "--onnxrt", + "--onnx-outputs", + "mark", + "all", + "--onnx-exclude-outputs", + "identity_out_2", + "--save-outputs", + outfile0.name, + ] + ) results = load_json(outfile0.name) [result] = list(results.values())[0] assert len(result) == 1 assert "identity_out_0" in result - def test_external_data(self): model = ONNX_MODELS["ext_weights"] - assert run_polygraphy_run([model.path, "--onnxrt", "--load-external-data", model.ext_data]) + assert run_polygraphy_run([model.path, "--onnxrt", "--external-data-dir", model.ext_data]) class TestOther(object): @@ -293,30 +395,44 @@ class TestOther(object): def test_subprocess_sanity(self): run_polygraphy_run([ONNX_MODELS["identity"].path, "--onnxrt", "--use-subprocess"]) - def test_custom_tolerance(self): - run_polygraphy_run([ONNX_MODELS["identity"].path, "--onnxrt", "--onnxrt", "--iterations=0", "--atol=1.0", "--rtol=1.0"]) - + run_polygraphy_run( + [ONNX_MODELS["identity"].path, "--onnxrt", "--onnxrt", "--iterations=0", "--atol=1.0", "--rtol=1.0"] + ) def test_custom_per_output_tolerance(self): - run_polygraphy_run([ONNX_MODELS["identity_identity"].path, "--onnxrt", "--onnxrt", "--onnx-outputs", "mark", "all", - "--atol", "identity_out_0:1.0", "identity_out_2:3.0", "0.5", - "--rtol", "identity_out_0:1.0", "identity_out_2:3.0", "0.5"]) - + run_polygraphy_run( + [ + ONNX_MODELS["identity_identity"].path, + "--onnxrt", + "--onnxrt", + "--onnx-outputs", + "mark", + "all", + "--atol", + "identity_out_0:1.0", + "identity_out_2:3.0", + "0.5", + "--rtol", + "identity_out_0:1.0", + "identity_out_2:3.0", + "0.5", + ] + ) def test_custom_input_ranges(self): - run_polygraphy_run([ONNX_MODELS["identity_identity"].path, "--onnxrt", - "--val-range", "X:[1.0,2.0]", "[0.5,1.5]"]) - + run_polygraphy_run( + [ONNX_MODELS["identity_identity"].path, "--onnxrt", "--val-range", "X:[1.0,2.0]", "[0.5,1.5]"] + ) def test_top_k(self): run_polygraphy_run([ONNX_MODELS["identity"].path, "--onnxrt", "--top-k=5"]) - @pytest.mark.parametrize("check_error_stat", ["max", "median", "mean"]) def test_check_error_stat(self, check_error_stat): - run_polygraphy_run([ONNX_MODELS["identity"].path, "--onnxrt", "--onnxrt", "--check-error-stat", check_error_stat]) - + run_polygraphy_run( + [ONNX_MODELS["identity"].path, "--onnxrt", "--onnxrt", "--check-error-stat", check_error_stat] + ) def test_save_load_outputs(self, tmp_path): OUTFILE0 = os.path.join(tmp_path, "outputs0.json") @@ -324,28 +440,45 @@ class TestOther(object): run_polygraphy_run([ONNX_MODELS["identity"].path, "--onnxrt", "--save-outputs", OUTFILE0]) run_polygraphy_run([ONNX_MODELS["identity"].path, "--onnxrt", "--save-outputs", OUTFILE1]) - status = run_polygraphy_run([ONNX_MODELS["identity"].path, "--onnxrt", "--load-results", OUTFILE0, OUTFILE1]) - assert "Difference is within tolerance" in status.stdout + status.stderr # Make sure it actually compared stuff. + status = run_polygraphy_run([ONNX_MODELS["identity"].path, "--onnxrt", "--load-outputs", OUTFILE0, OUTFILE1]) + assert ( + "Difference is within tolerance" in status.stdout + status.stderr + ) # Make sure it actually compared stuff. # Should work with only one file - status = run_polygraphy_run([ONNX_MODELS["identity"].path, "--load-results", OUTFILE0]) - assert "Difference is within tolerance" not in status.stdout + status.stderr # Make sure it DIDN'T compare stuff. + status = run_polygraphy_run([ONNX_MODELS["identity"].path, "--load-outputs", OUTFILE0]) + assert ( + "Difference is within tolerance" not in status.stdout + status.stderr + ) # Make sure it DIDN'T compare stuff. # Should work even with no runners specified - status = run_polygraphy_run([ONNX_MODELS["identity"].path, "--load-results", OUTFILE0, OUTFILE1]) - assert "Difference is within tolerance" in status.stdout + status.stderr # Make sure it actually compared stuff. + status = run_polygraphy_run([ONNX_MODELS["identity"].path, "--load-outputs", OUTFILE0, OUTFILE1]) + assert ( + "Difference is within tolerance" in status.stdout + status.stderr + ) # Make sure it actually compared stuff. # Should work even when comparing a single runner to itself. - status = run_polygraphy_run([ONNX_MODELS["identity"].path, "--load-results", OUTFILE0, OUTFILE0]) - assert "Difference is within tolerance" in status.stdout + status.stderr # Make sure it actually compared stuff. - + status = run_polygraphy_run([ONNX_MODELS["identity"].path, "--load-outputs", OUTFILE0, OUTFILE0]) + assert ( + "Difference is within tolerance" in status.stdout + status.stderr + ) # Make sure it actually compared stuff. def test_save_load_inputs(self): with tempfile.NamedTemporaryFile() as infile0, tempfile.NamedTemporaryFile() as infile1: run_polygraphy_run([ONNX_MODELS["identity"].path, "--onnxrt", "--save-input-data", infile0.name]) - run_polygraphy_run([ONNX_MODELS["identity"].path, "--onnxrt", "--load-input-data", infile0.name, "--save-input-data", infile1.name]) # Copy - run_polygraphy_run([ONNX_MODELS["identity"].path, "--onnxrt", "--load-input-data", infile0.name, infile1.name]) - + run_polygraphy_run( + [ + ONNX_MODELS["identity"].path, + "--onnxrt", + "--load-input-data", + infile0.name, + "--save-input-data", + infile1.name, + ] + ) # Copy + run_polygraphy_run( + [ONNX_MODELS["identity"].path, "--onnxrt", "--load-input-data", infile0.name, infile1.name] + ) @pytest.mark.skipif(mod.version(trt.__version__) < mod.version("7.0"), reason="Unsupported for TRT 6") def test_runner_coexistence(self): diff --git a/tools/Polygraphy/tests/tools/test_script.py b/tools/Polygraphy/tests/tools/test_script.py index 47c2facc..d3fc1186 100644 --- a/tools/Polygraphy/tests/tools/test_script.py +++ b/tools/Polygraphy/tests/tools/test_script.py @@ -1,4 +1,3 @@ - # # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. # @@ -17,49 +16,52 @@ import pytest from polygraphy.exception import PolygraphyInternalException -from polygraphy.tools.script import (Script, inline, make_invocable, - make_invocable_if_nondefault) +from polygraphy.tools.script import Script, inline, make_invocable, make_invocable_if_nondefault def make_test_string(): return Script.String("test") + class TestScript(object): - @pytest.mark.parametrize("func", [ - lambda _: inline(make_test_string()), - lambda s: s.add_loader(make_test_string(), make_test_string()), - lambda s: s.add_runner(make_test_string()), - lambda s: s.append_preimport(make_test_string()), - lambda s: s.append_suffix(make_test_string()), - lambda s: s.set_data_loader(make_test_string()), - ]) + @pytest.mark.parametrize( + "func", + [ + lambda _: inline(make_test_string()), + lambda s: s.add_loader(make_test_string(), make_test_string()), + lambda s: s.add_runner(make_test_string()), + lambda s: s.append_preimport(make_test_string()), + lambda s: s.append_suffix(make_test_string()), + lambda s: s.set_data_loader(make_test_string()), + ], + ) def test_add_funcs_fail_on_unsafe(self, func): script = Script() with pytest.raises(PolygraphyInternalException, match="was not checked for safety"): func(script) - - @pytest.mark.parametrize("case, expected", [ - ("should_become_raw", "'should_become_raw'"), - ("parens))", r"'parens))'"), - ("'squotes'", "\"'squotes'\""), - ('"dquotes"', '\'"dquotes"\''), - (r"braces{}{})", r"'braces{}{})'"), - ("commas, ,", r"'commas, ,'"), - ("escape_quote_with_backslash\'", "\"escape_quote_with_backslash\'\""), - ("unterm_in_quotes_ok))", r"'unterm_in_quotes_ok))'"), - ]) + @pytest.mark.parametrize( + "case, expected", + [ + ("should_become_raw", "'should_become_raw'"), + ("parens))", r"'parens))'"), + ("'squotes'", "\"'squotes'\""), + ('"dquotes"', "'\"dquotes\"'"), + (r"braces{}{})", r"'braces{}{})'"), + ("commas, ,", r"'commas, ,'"), + ("escape_quote_with_backslash'", '"escape_quote_with_backslash\'"'), + ("unterm_in_quotes_ok))", r"'unterm_in_quotes_ok))'"), + ], + ) def test_non_inlined_strings_escaped(self, case, expected): out = make_invocable("Dummy", case, x=case) ex_out = "Dummy({:}, x={:})".format(expected, expected) assert out.unwrap() == ex_out - def test_invoke_none_args(self): assert make_invocable("Dummy", None).unwrap() == "Dummy(None)" assert make_invocable("Dummy", x=None).unwrap() == "Dummy()" - def test_invoke_if_nondefault_none_args(self): assert make_invocable_if_nondefault("Dummy", None) is None assert make_invocable_if_nondefault("Dummy", x=None) is None diff --git a/tools/Polygraphy/tests/tools/test_surgeon.py b/tools/Polygraphy/tests/tools/test_surgeon.py index 256177c3..bf17cead 100644 --- a/tools/Polygraphy/tests/tools/test_surgeon.py +++ b/tools/Polygraphy/tests/tools/test_surgeon.py @@ -13,12 +13,13 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import os import tempfile import onnx import onnx_graphsurgeon as gs import pytest -from tests.helper import check_file_non_empty +from tests.helper import is_file_non_empty from tests.models.meta import ONNX_MODELS from tests.tools.common import run_polygraphy_run, run_polygraphy_surgeon @@ -34,31 +35,58 @@ def was_shape_inference_run(status): class TestSurgeonExtract(object): def test_no_shape_inference_if_has_metadata(self): with tempfile.NamedTemporaryFile() as outmodel: - status = run_polygraphy_surgeon(["extract", ONNX_MODELS["identity_identity"].path, "-o", outmodel.name, - "--inputs", "X:auto:auto"]) + status = run_polygraphy_surgeon( + ["extract", ONNX_MODELS["identity_identity"].path, "-o", outmodel.name, "--inputs", "X:auto:auto"] + ) onnx_model_sanity_check(outmodel.name) assert not was_shape_inference_run(status) - def test_onnx_shape_inference_if_no_metadata(self): with tempfile.NamedTemporaryFile() as outmodel: - status = run_polygraphy_surgeon(["extract", ONNX_MODELS["identity_identity"].path, "-o", outmodel.name, - "--inputs", "identity_out_0:auto:auto"]) + status = run_polygraphy_surgeon( + [ + "extract", + ONNX_MODELS["identity_identity"].path, + "-o", + outmodel.name, + "--inputs", + "identity_out_0:auto:auto", + ] + ) onnx_model_sanity_check(outmodel.name) assert was_shape_inference_run(status) - def test_fallback_shape_inference_no_onnx_shape_inference(self): with tempfile.NamedTemporaryFile() as outmodel: - status = run_polygraphy_surgeon(["extract", ONNX_MODELS["identity_identity"].path, "-o", outmodel.name, "--inputs", - "identity_out_0:auto:auto", "--outputs", "identity_out_2:auto", "--force-fallback-shape-inference"]) + status = run_polygraphy_surgeon( + [ + "extract", + ONNX_MODELS["identity_identity"].path, + "-o", + outmodel.name, + "--inputs", + "identity_out_0:auto:auto", + "--outputs", + "identity_out_2:auto", + "--force-fallback-shape-inference", + ] + ) onnx_model_sanity_check(outmodel.name) assert not was_shape_inference_run(status) - def test_force_fallback_shape_inference_will_override_model_shapes(self): with tempfile.NamedTemporaryFile() as outmodel: - run_polygraphy_surgeon(["extract", ONNX_MODELS["dynamic_identity"].path, "-o", outmodel.name, "--outputs", "Y:auto", "--force-fallback-shape-inference"]) + run_polygraphy_surgeon( + [ + "extract", + ONNX_MODELS["dynamic_identity"].path, + "-o", + outmodel.name, + "--outputs", + "Y:auto", + "--force-fallback-shape-inference", + ] + ) onnx_model_sanity_check(outmodel.name) graph = gs.import_onnx(onnx.load(outmodel.name)) # Inputs should become fixed since fallback shape inference is being forced. @@ -67,7 +95,6 @@ class TestSurgeonExtract(object): assert tuple(graph.inputs[0].shape) == (1, 2, 1, 1) assert tuple(graph.outputs[0].shape) == (1, 2, 1, 1) - def test_sanity_dim_param(self): with tempfile.NamedTemporaryFile() as outmodel: run_polygraphy_surgeon(["extract", ONNX_MODELS["dim_param"].path, "-o", outmodel.name]) @@ -86,53 +113,105 @@ class TestSurgeonInsert(object): assert graph_input_names == set(expected_graph_input_names) return model - def test_insert_at_tensor(self): # Insert a new node in between existing nodes without replacing any existing nodes. with tempfile.NamedTemporaryFile() as outmodel: - run_polygraphy_surgeon(["insert", ONNX_MODELS["identity_identity"].path, "-o", outmodel.name, "--inputs=identity_out_0", - "--outputs=identity_out_0", "--op=FakeOp"]) + run_polygraphy_surgeon( + [ + "insert", + ONNX_MODELS["identity_identity"].path, + "-o", + outmodel.name, + "--inputs=identity_out_0", + "--outputs=identity_out_0", + "--op=FakeOp", + ] + ) self.check_insert_model(outmodel.name, ["Identity", "FakeOp", "Identity"], ["X"], ["identity_out_2"]) - def test_graph_output(self): # FakeOp output tensor should be marked as a graph output. Name should be preserved - identity_out_2 with tempfile.NamedTemporaryFile() as outmodel: - run_polygraphy_surgeon(["insert", ONNX_MODELS["identity_identity"].path, "-o", outmodel.name, "--inputs=identity_out_2", - "--outputs=identity_out_2", "--op=FakeOp"]) + run_polygraphy_surgeon( + [ + "insert", + ONNX_MODELS["identity_identity"].path, + "-o", + outmodel.name, + "--inputs=identity_out_2", + "--outputs=identity_out_2", + "--op=FakeOp", + ] + ) self.check_insert_model(outmodel.name, ["Identity", "Identity", "FakeOp"], ["X"], ["identity_out_2"]) - def test_at_graph_input(self): with tempfile.NamedTemporaryFile() as outmodel: - run_polygraphy_surgeon(["insert", ONNX_MODELS["identity_identity"].path, "-o", outmodel.name, "--inputs=X", - "--outputs=X", "--op=FakeOp"]) + run_polygraphy_surgeon( + [ + "insert", + ONNX_MODELS["identity_identity"].path, + "-o", + outmodel.name, + "--inputs=X", + "--outputs=X", + "--op=FakeOp", + ] + ) self.check_insert_model(outmodel.name, ["FakeOp", "Identity", "Identity"], ["X"], ["identity_out_2"]) - # When a specified input tensor is used by multiple other nodes, it should not be # disconnected from other nodes. def test_multi_use_input(self): with tempfile.NamedTemporaryFile() as outmodel: - run_polygraphy_surgeon(["insert", ONNX_MODELS["reducable"].path, "-o", outmodel.name, "--inputs=add_out_4", - "--outputs=identity_out_8", "--op=FakeOp"]) - model = self.check_insert_model(outmodel.name, ["Identity", "Identity", "Add", "FakeOp", "Identity"], ["X0", "Y0"], ["identity_out_6", "identity_out_8"]) + run_polygraphy_surgeon( + [ + "insert", + ONNX_MODELS["reducable"].path, + "-o", + outmodel.name, + "--inputs=add_out_4", + "--outputs=identity_out_8", + "--op=FakeOp", + ] + ) + model = self.check_insert_model( + outmodel.name, + ["Identity", "Identity", "Add", "FakeOp", "Identity"], + ["X0", "Y0"], + ["identity_out_6", "identity_out_8"], + ) other_branch_node = model.graph.node[-1] assert other_branch_node.name == "onnx_graphsurgeon_node_7" assert other_branch_node.input == ["add_out_4"] - def test_with_attributes(self): with tempfile.NamedTemporaryFile() as outmodel: # str_attr='0' should be interpreted as a string, not an int # float_attr=0.0 should be interpreted as a float, not an int # int_attr=0 should be interpreted as an int - run_polygraphy_surgeon(["insert", ONNX_MODELS["identity_identity"].path, "-o", outmodel.name, "--inputs=X", - "--outputs=X", "--op=FakeOp", - "--attrs", - "str_attr='0'", "int_attr=0", "float_attr=0.0", "other_str_attr=name", - "str_list_attr=['0','1']", "int_list_attr=[1,2,3]", "float_list_attr=[0.0,-1.0,-2.0]"]) - model = self.check_insert_model(outmodel.name, ["FakeOp", "Identity", "Identity"], ["X"], ["identity_out_2"]) + run_polygraphy_surgeon( + [ + "insert", + ONNX_MODELS["identity_identity"].path, + "-o", + outmodel.name, + "--inputs=X", + "--outputs=X", + "--op=FakeOp", + "--attrs", + "str_attr='0'", + "int_attr=0", + "float_attr=0.0", + "other_str_attr=name", + "str_list_attr=['0','1']", + "int_list_attr=[1,2,3]", + "float_list_attr=[0.0,-1.0,-2.0]", + ] + ) + model = self.check_insert_model( + outmodel.name, ["FakeOp", "Identity", "Identity"], ["X"], ["identity_out_2"] + ) node = model.graph.node[0] attrs = node.attribute @@ -159,25 +238,36 @@ class TestSurgeonInsert(object): class TestSurgeonSanitize(object): + @pytest.mark.parametrize("no_per_pass_shape_inf", [None, "--no-per-pass-shape-inference"]) @pytest.mark.parametrize("fold_shapes", [None, "--no-fold-shapes"]) @pytest.mark.parametrize("partitioning", [None, "basic", "recursive"]) - def test_fold_constants(self, partitioning, fold_shapes): + def test_fold_constants(self, no_per_pass_shape_inf, partitioning, fold_shapes): with tempfile.NamedTemporaryFile() as outmodel: cmd = ["sanitize", ONNX_MODELS["const_foldable"].path, "-o", outmodel.name, "--fold-constants"] if fold_shapes: cmd += [fold_shapes] if partitioning: cmd += ["--partitioning", partitioning] + if no_per_pass_shape_inf: + cmd += [no_per_pass_shape_inf] run_polygraphy_surgeon(cmd) onnx_model_sanity_check(outmodel.name) model = onnx.load(outmodel.name) assert len(model.graph.node) == 1 - def test_fold_constants_single_pass(self): with tempfile.NamedTemporaryFile() as outmodel: - status = run_polygraphy_surgeon(["sanitize", ONNX_MODELS["const_foldable"].path, "-o", outmodel.name, "--fold-constants", "--num-passes=1"]) + status = run_polygraphy_surgeon( + [ + "sanitize", + ONNX_MODELS["const_foldable"].path, + "-o", + outmodel.name, + "--fold-constants", + "--num-passes=1", + ] + ) assert "Pass 1" in status.stdout assert "Pass 2" not in status.stdout @@ -186,11 +276,16 @@ class TestSurgeonSanitize(object): model = onnx.load(outmodel.name) assert len(model.graph.node) == 1 - @pytest.mark.parametrize("new_dim", [1, 2, 3]) def test_override_shapes(self, new_dim): with tempfile.NamedTemporaryFile() as outmodel: - cmd = ["sanitize", ONNX_MODELS["dynamic_identity"].path, "-o", outmodel.name, "--override-input-shapes=X:[1,2,{new_dim},{new_dim}]".format(new_dim=new_dim)] + cmd = [ + "sanitize", + ONNX_MODELS["dynamic_identity"].path, + "-o", + outmodel.name, + "--override-input-shapes=X:[1,2,{new_dim},{new_dim}]".format(new_dim=new_dim), + ] run_polygraphy_surgeon(cmd) onnx_model_sanity_check(outmodel.name) @@ -203,56 +298,112 @@ class TestSurgeonSanitize(object): assert shape == [1, 2, new_dim, new_dim] - def test_override_shapes_no_clear_const_tensors_meta(self): with tempfile.NamedTemporaryFile() as outmodel: - run_polygraphy_surgeon(["sanitize", ONNX_MODELS["const_foldable"].path, "-o", outmodel.name, "--override-input-shapes=input:[1,3]"]) - + run_polygraphy_surgeon( + [ + "sanitize", + ONNX_MODELS["const_foldable"].path, + "-o", + outmodel.name, + "--override-input-shapes=input:[1,3]", + ] + ) def test_override_shapes_partial_inputs(self): with tempfile.NamedTemporaryFile() as outmodel: - run_polygraphy_surgeon(["sanitize", ONNX_MODELS["dynamic_identity"].path, "-o", outmodel.name, "--override-input-shapes=Y:[1,2,3,4]"]) + run_polygraphy_surgeon( + [ + "sanitize", + ONNX_MODELS["dynamic_identity"].path, + "-o", + outmodel.name, + "--override-input-shapes=Y:[1,2,3,4]", + ] + ) model = onnx.load(outmodel.name) assert model.graph.input[0].type.tensor_type.shape.dim[2].dim_param == "height" assert model.graph.input[0].type.tensor_type.shape.dim[3].dim_param == "width" - def test_override_shapes_no_reorder(self): with tempfile.NamedTemporaryFile() as outmodel: - run_polygraphy_surgeon(["sanitize", ONNX_MODELS["reducable"].path, "-o", outmodel.name, "--override-input-shapes", "Y0:[5]", "X0:[5]"]) + run_polygraphy_surgeon( + [ + "sanitize", + ONNX_MODELS["reducable"].path, + "-o", + outmodel.name, + "--override-input-shapes", + "Y0:[5]", + "X0:[5]", + ] + ) model = onnx.load(outmodel.name) assert model.graph.input[0].name == "X0" assert model.graph.input[1].name == "Y0" - def test_modify_onnx_outputs(self): with tempfile.NamedTemporaryFile(suffix=".onnx") as outmodel: - run_polygraphy_surgeon(["sanitize", ONNX_MODELS["identity_identity"].path, "-o", outmodel.name, "--outputs", "mark", "all"]) + run_polygraphy_surgeon( + ["sanitize", ONNX_MODELS["identity_identity"].path, "-o", outmodel.name, "--outputs", "mark", "all"] + ) model = onnx.load(outmodel.name) assert len(model.graph.output) == 2 - def test_cleanup(self): with tempfile.NamedTemporaryFile(suffix=".onnx") as outmodel: - run_polygraphy_surgeon(["sanitize", ONNX_MODELS["identity_identity"].path, "-o", outmodel.name, "--outputs", "identity_out_0", "--cleanup"]) + run_polygraphy_surgeon( + [ + "sanitize", + ONNX_MODELS["identity_identity"].path, + "-o", + outmodel.name, + "--outputs", + "identity_out_0", + "--cleanup", + ] + ) model = onnx.load(outmodel.name) assert len(model.graph.node) == 1 assert model.graph.output[0].name == "identity_out_0" - def test_external_data(self): - with tempfile.NamedTemporaryFile(suffix=".onnx") as outmodel, tempfile.NamedTemporaryFile() as data: + with tempfile.TemporaryDirectory() as outdir: model = ONNX_MODELS["ext_weights"] - assert run_polygraphy_surgeon(["sanitize", model.path, "-o", outmodel.name, "--load-external-data", model.ext_data, "--save-external-data", data.name, "-vvvvv"]) - check_file_non_empty(outmodel.name) - check_file_non_empty(data.name) - + outmodel = os.path.join(outdir, "out_model.onnx") + outdata = "ext_weights.data" + assert run_polygraphy_surgeon( + [ + "sanitize", + model.path, + "--external-data-dir", + model.ext_data, + "--fold-constants", + "-o", + outmodel, + "--save-external-data", + outdata, + "--external-data-size-threshold=0", + "-vvvvv", + ] + ) + assert is_file_non_empty(outmodel) + assert is_file_non_empty(os.path.join(outdir, outdata)) + assert run_polygraphy_run([outmodel, "--onnxrt", "--external-data-dir", outdir]) def test_force_fallback_shape_inference_will_override_model_shapes(self): with tempfile.NamedTemporaryFile() as outmodel: - status = run_polygraphy_surgeon(["sanitize", ONNX_MODELS["dynamic_identity"].path, "-o", outmodel.name, "--force-fallback-shape-inference"]) + status = run_polygraphy_surgeon( + [ + "sanitize", + ONNX_MODELS["dynamic_identity"].path, + "-o", + outmodel.name, + "--force-fallback-shape-inference", + ] + ) onnx_model_sanity_check(outmodel.name) graph = gs.import_onnx(onnx.load(outmodel.name)) # Inputs should become fixed since fallback shape inference is being forced. diff --git a/tools/Polygraphy/tests/tools/test_template.py b/tools/Polygraphy/tests/tools/test_template.py index 3ead8e92..dd3cd520 100644 --- a/tools/Polygraphy/tests/tools/test_template.py +++ b/tools/Polygraphy/tests/tools/test_template.py @@ -32,7 +32,6 @@ class TestTrtNetwork(object): assert isinstance(builder, trt.Builder) assert isinstance(network, trt.INetworkDefinition) - def test_with_model_file(self): with tempfile.NamedTemporaryFile("w+", suffix=".py") as template: run_polygraphy_template(["trt-network", ONNX_MODELS["identity"].path, "-o", template.name]) diff --git a/tools/Polygraphy/tests/util/test_format.py b/tools/Polygraphy/tests/util/test_format.py index d89f542e..b5f2018a 100644 --- a/tools/Polygraphy/tests/util/test_format.py +++ b/tools/Polygraphy/tests/util/test_format.py @@ -25,6 +25,7 @@ class FormatTestCase: self.shape = shape self.format = format + EXPECTED_FORMATS = [ FormatTestCase((1, 3, 480, 960), DataFormat.NCHW), FormatTestCase((1, 3, 224, 224), DataFormat.NCHW), @@ -32,6 +33,7 @@ EXPECTED_FORMATS = [ FormatTestCase((1, 9, 9, 3), DataFormat.NHWC), ] + @pytest.mark.parametrize("test_case", EXPECTED_FORMATS) def test_format_deduction(test_case): assert test_case.format == FormatManager.determine_format(test_case.shape) diff --git a/tools/Polygraphy/tests/util/test_serde.py b/tools/Polygraphy/tests/util/test_serde.py index 32a275ea..42f197ea 100644 --- a/tools/Polygraphy/tests/util/test_serde.py +++ b/tools/Polygraphy/tests/util/test_serde.py @@ -37,7 +37,7 @@ def encode(dummy): @Decoder.register(Dummy) def decode(dct): - assert len(dct) == 1 # Custom type markers should be removed at this point + assert len(dct) == 1 # Custom type markers should be removed at this point return Dummy(x=dct["x"]) @@ -45,8 +45,8 @@ class TestEncoder(object): def test_registered(self): d = Dummy(x=-1) d_json = to_json(d) - assert encode(d) == {'x': d.x, '__polygraphy_encoded_Dummy': constants.TYPE_MARKER} - expected = "{{\n \"x\": {:},\n \"__polygraphy_encoded_Dummy\": \"{:}\"\n}}".format(d.x, constants.TYPE_MARKER) + assert encode(d) == {"x": d.x, constants.TYPE_MARKER: "Dummy"} + expected = '{{\n "x": {:},\n "{:}": "Dummy"\n}}'.format(d.x, constants.TYPE_MARKER) assert d_json == expected @@ -60,14 +60,23 @@ class TestDecoder(object): def make_algo(): - return Algorithm(implementation=4, tactic=5, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], - outputs=[(trt.TensorFormat.LINEAR, trt.float32)]) + return Algorithm( + implementation=4, + tactic=5, + inputs=[(trt.TensorFormat.LINEAR, trt.float32)], + outputs=[(trt.TensorFormat.LINEAR, trt.float32)], + ) + def make_iter_result(): - return IterationResult(runtime=4.5, runner_name="test", outputs={ - "out0": np.random.random_sample((1, 2, 1)), - "out1": np.ones((1, 2), dtype=np.float32), - }) + return IterationResult( + runtime=4.5, + runner_name="test", + outputs={ + "out0": np.random.random_sample((1, 2, 1)), + "out1": np.ones((1, 2), dtype=np.float32), + }, + ) JSONABLE_CASES = [ @@ -75,19 +84,32 @@ JSONABLE_CASES = [ TacticReplayData().add("hi", algorithm=make_algo()), ] + class TestImplementations(object): - @pytest.mark.parametrize("obj", [ - Algorithm(implementation=4, tactic=5, inputs=[(trt.TensorFormat.LINEAR, trt.float32)], - outputs=[(trt.TensorFormat.LINEAR, trt.float32)]), - Algorithm(implementation=4, tactic=5, inputs=[(trt.TensorFormat.LINEAR, trt.float32), (trt.TensorFormat.CHW32, trt.int8)], - outputs=[(trt.TensorFormat.CHW32, trt.float16)]), - np.ones((3, 4, 5), dtype=np.int64), - np.ones(5, dtype=np.int64), - np.zeros((4, 5), dtype=np.float32), - np.random.random_sample((3, 5)), - make_iter_result(), - RunResults([("runner0", [make_iter_result()]), ("runner0", [make_iter_result()])]) - ], ids=lambda x: type(x)) + @pytest.mark.parametrize( + "obj", + [ + Algorithm( + implementation=4, + tactic=5, + inputs=[(trt.TensorFormat.LINEAR, trt.float32)], + outputs=[(trt.TensorFormat.LINEAR, trt.float32)], + ), + Algorithm( + implementation=4, + tactic=5, + inputs=[(trt.TensorFormat.LINEAR, trt.float32), (trt.TensorFormat.CHW32, trt.int8)], + outputs=[(trt.TensorFormat.CHW32, trt.float16)], + ), + np.ones((3, 4, 5), dtype=np.int64), + np.ones(5, dtype=np.int64), + np.zeros((4, 5), dtype=np.float32), + np.random.random_sample((3, 5)), + make_iter_result(), + RunResults([("runner0", [make_iter_result()]), ("runner0", [make_iter_result()])]), + ], + ids=lambda x: type(x), + ) def test_serde(self, obj): encoded = to_json(obj) decoded = from_json(encoded) @@ -96,14 +118,12 @@ class TestImplementations(object): else: assert decoded == obj - @pytest.mark.parametrize("obj", JSONABLE_CASES) def test_to_from_json(self, obj): encoded = obj.to_json() decoded = type(obj).from_json(encoded) assert decoded == obj - @pytest.mark.parametrize("obj", JSONABLE_CASES) def test_save_load(self, obj): with tempfile.NamedTemporaryFile("w+") as f: @@ -111,7 +131,6 @@ class TestImplementations(object): decoded = type(obj).load(f) assert decoded == obj - def test_cannot_save_load_to_different_types(self): run_result = JSONABLE_CASES[0] encoded = run_result.to_json() diff --git a/tools/Polygraphy/tests/util/test_util.py b/tools/Polygraphy/tests/util/test_util.py index d8929b75..79e01b4f 100644 --- a/tools/Polygraphy/tests/util/test_util.py +++ b/tools/Polygraphy/tests/util/test_util.py @@ -17,12 +17,14 @@ import numpy as np import pytest from polygraphy import util + VOLUME_CASES = [ ((1, 1, 1), 1), ((2, 3, 4), 24), (tuple(), 1), ] + @pytest.mark.parametrize("case", VOLUME_CASES) def test_volume(case): it, vol = case @@ -36,11 +38,23 @@ class FindInDictCase(object): self.index = index self.expected = expected + FIND_IN_DICT_CASES = [ - FindInDictCase("resnet50_v1.5/output/Softmax:0", map={"resnet50_v1.5/output/Softmax:0": "x"}, index=None, expected="resnet50_v1.5/output/Softmax:0"), - FindInDictCase("resnet50_v1.5/output/Softmax:0", map={"resnet50_v1.5/output/softmax:0": "x"}, index=None, expected="resnet50_v1.5/output/softmax:0"), + FindInDictCase( + "resnet50_v1.5/output/Softmax:0", + map={"resnet50_v1.5/output/Softmax:0": "x"}, + index=None, + expected="resnet50_v1.5/output/Softmax:0", + ), + FindInDictCase( + "resnet50_v1.5/output/Softmax:0", + map={"resnet50_v1.5/output/softmax:0": "x"}, + index=None, + expected="resnet50_v1.5/output/softmax:0", + ), ] + @pytest.mark.parametrize("case", FIND_IN_DICT_CASES) def test_find_in_dict(case): actual = util.find_in_dict(case.name, case.map, case.index) @@ -51,35 +65,44 @@ SHAPE_OVERRIDE_CASES = [ ((1, 3, 224, 224), (None, 3, 224, 224), True), ] + @pytest.mark.parametrize("case", SHAPE_OVERRIDE_CASES) def test_is_valid_shape_override(case): override, shape, expected = case assert util.is_valid_shape_override(new_shape=override, original_shape=shape) == expected +def arange(shape): + return np.arange(util.volume(shape)).reshape(shape) + + SHAPE_MATCHING_CASES = [ - (np.zeros((1, 1, 3, 3)), (3, 3), (3, 3)), # Squeeze array shape - (np.zeros((1, 3, 3, 1)), (1, 1, 3, 3), (1, 1, 3, 3)), # Permute - (np.zeros((3, 3)), (1, 1, 3, 3), (3, 3)), # Squeeze specified shape - (np.zeros((3, 3)), (-1, 3), (3, 3)), # Infer dynamic - (np.zeros((3 * 224 * 224)), (None, 3, 224, 224), (1, 3, 224, 224)), # Reshape and Permute - (np.zeros((1, 3, 224, 224)), (None, 224, 224, 3), (1, 224, 224, 3)), # Permute + (arange((1, 1, 3, 3)), (3, 3), arange((3, 3))), # Squeeze array shape + ( + arange((1, 3, 3, 1)), + (1, 1, 3, 3), + arange((1, 1, 3, 3)), + ), # Permutation should make no difference as other dimensions are 1s + (arange((3, 3)), (1, 1, 3, 3), arange((1, 1, 3, 3))), # Unsqueeze where needed + (arange((3, 3)), (-1, 3), arange((3, 3))), # Infer dynamic + (arange((3 * 2 * 2,)), (None, 3, 2, 2), arange((1, 3, 2, 2))), # Reshape with inferred dimension + (arange((1, 3, 2, 2)), (None, 2, 2, 3), np.transpose(arange((1, 3, 2, 2)), [0, 2, 3, 1])), # Permute ] -@pytest.mark.parametrize("case", SHAPE_MATCHING_CASES) -def test_shape_matching(case): - out, shape, expected_shape = case - out = util.try_match_shape(out, shape) - assert out.shape == expected_shape +@pytest.mark.parametrize("arr, shape, expected", SHAPE_MATCHING_CASES) +def test_shape_matching(arr, shape, expected): + arr = util.try_match_shape(arr, shape) + assert np.array_equal(arr, expected) UNPACK_ARGS_CASES = [ - ((0, 1, 2), 3, (0, 1, 2)), # no extras - ((0, 1, 2), 4, (0, 1, 2, None)), # 1 extra - ((0, 1, 2), 2, (0, 1)), # 1 fewer + ((0, 1, 2), 3, (0, 1, 2)), # no extras + ((0, 1, 2), 4, (0, 1, 2, None)), # 1 extra + ((0, 1, 2), 2, (0, 1)), # 1 fewer ] + @pytest.mark.parametrize("case", UNPACK_ARGS_CASES) def test_unpack_args(case): args, num, expected = case @@ -94,6 +117,7 @@ UNIQUE_LIST_CASES = [ ([5, 5, 5, 5, 5], [5]), ] + @pytest.mark.parametrize("case", UNIQUE_LIST_CASES) def test_unique_list(case): lst, expected = case diff --git a/tools/onnx-graphsurgeon/CHANGELOG.md b/tools/onnx-graphsurgeon/CHANGELOG.md index 392ed96e..b2ed0ba3 100644 --- a/tools/onnx-graphsurgeon/CHANGELOG.md +++ b/tools/onnx-graphsurgeon/CHANGELOG.md @@ -3,6 +3,11 @@ Dates are in YYYY-MM-DD format. +## v0.3.10 (2021-05-20) +### Added +- Added support for folding `Shape -> Slice` patterns even when the entire shape may not be known. + + ## v0.3.9 (2021-04-20) ### Changed - `fold_constants()` will no longer store values for foldable tensors whose outputs are all foldable. diff --git a/tools/onnx-graphsurgeon/onnx_graphsurgeon/__init__.py b/tools/onnx-graphsurgeon/onnx_graphsurgeon/__init__.py index 835b65a5..be1fe448 100644 --- a/tools/onnx-graphsurgeon/onnx_graphsurgeon/__init__.py +++ b/tools/onnx-graphsurgeon/onnx_graphsurgeon/__init__.py @@ -5,4 +5,4 @@ from onnx_graphsurgeon.ir.node import Node from onnx_graphsurgeon.ir.tensor import Constant, Tensor, Variable from onnx_graphsurgeon.util.exception import OnnxGraphSurgeonException -__version__ = "0.3.9" +__version__ = "0.3.10" diff --git a/tools/onnx-graphsurgeon/onnx_graphsurgeon/exporters/onnx_exporter.py b/tools/onnx-graphsurgeon/onnx_graphsurgeon/exporters/onnx_exporter.py index 8094573d..43611fb1 100644 --- a/tools/onnx-graphsurgeon/onnx_graphsurgeon/exporters/onnx_exporter.py +++ b/tools/onnx-graphsurgeon/onnx_graphsurgeon/exporters/onnx_exporter.py @@ -113,9 +113,10 @@ def export_onnx(graph: Graph, do_type_check=True, **kwargs) -> "onnx.ModelProto" """ onnx_graph = OnnxExporter.export_graph(graph, do_type_check=do_type_check) - if graph.import_domains is None: - kwargs["opset_imports"] = [onnx.helper.make_opsetid("", graph.opset)] - else: - kwargs["opset_imports"] = graph.import_domains + if "opset_imports" not in kwargs: + if graph.import_domains is None: + kwargs["opset_imports"] = [onnx.helper.make_opsetid("", graph.opset)] + else: + kwargs["opset_imports"] = graph.import_domains return onnx.helper.make_model(onnx_graph, **kwargs) diff --git a/tools/onnx-graphsurgeon/onnx_graphsurgeon/ir/graph.py b/tools/onnx-graphsurgeon/onnx_graphsurgeon/ir/graph.py index 7eb071be..6c108a6f 100644 --- a/tools/onnx-graphsurgeon/onnx_graphsurgeon/ir/graph.py +++ b/tools/onnx-graphsurgeon/onnx_graphsurgeon/ir/graph.py @@ -588,9 +588,53 @@ class Graph(object): return np.array(shape, dtype=np.int64) + def handle_shape_slice(tensor): + slice = get_producer(tensor, "Slice") + if slice is None: + return None + + data = slice.inputs[0] + starts, ends = slice.inputs[1:3] + + inp = get_input(get_producer(data, "Shape")) + if inp is None or inp.shape is None: + return None + + if any(not isinstance(t, Constant) for t in [starts, ends]): + return None + + def get_value(tensor): # Gets the integer value of a tensor with a single item + if not tensor.shape: + return tensor.values + else: + return list(tensor.values)[0] + + if len(slice.inputs) > 3: + axes = slice.inputs[3] + if not isinstance(axes, Constant): + return None + + if get_value(axes) != 0: + return None + + steps = 1 + if len(slice.inputs) > 4: + steps = slice.inputs[4] + if not isinstance(steps, Constant): + return None + + steps = get_value(steps) + + shape = inp.shape[get_value(starts):get_value(ends):steps] + if misc.is_dynamic_shape(shape): + return None + + return np.array(shape, dtype=np.int64) + + # Finds the static shape of a shape node output if possible, otherwise returns None. def lower_shape(tensor): - SHAPE_FOLD_FUNCS = [handle_shape, handle_shape_gather] + SHAPE_FOLD_FUNCS = [handle_shape_gather, handle_shape_slice, handle_shape] for fold_func in SHAPE_FOLD_FUNCS: shape = fold_func(tensor) if shape is not None: diff --git a/tools/onnx-graphsurgeon/onnx_graphsurgeon/ir/node.py b/tools/onnx-graphsurgeon/onnx_graphsurgeon/ir/node.py index 5d6e761a..0463f980 100644 --- a/tools/onnx-graphsurgeon/onnx_graphsurgeon/ir/node.py +++ b/tools/onnx-graphsurgeon/onnx_graphsurgeon/ir/node.py @@ -112,7 +112,19 @@ class Node(object): def __str__(self): - ret = "{:} ({:})\n\tInputs: {:}\n\tOutputs: {:}".format(self.name, self.op, self.inputs, self.outputs) + ret = "{:} ({:})".format(self.name, self.op) + + def add_io(name, io): + nonlocal ret + ret += "\n\t{:}: [".format(name) + for elem in io: + ret += "\n\t\t{:}".format(elem) + ret += "\n\t]" + + + add_io("Inputs", self.inputs) + add_io("Outputs", self.outputs) + if self.attrs: ret += "\nAttributes: {:}".format(self.attrs) return ret diff --git a/tools/onnx-graphsurgeon/tests/ir/test_graph.py b/tools/onnx-graphsurgeon/tests/ir/test_graph.py index 930dae6c..ec467bdc 100644 --- a/tools/onnx-graphsurgeon/tests/ir/test_graph.py +++ b/tools/onnx-graphsurgeon/tests/ir/test_graph.py @@ -70,6 +70,11 @@ def gather(self, data, indices): return self.layer(op="Gather", inputs=[data, indices], outputs=["gather_out"])[0] +@gs.Graph.register() +def slice(self, data, starts, ends, axes, steps): + return self.layer(op="Slice", inputs=[data, starts, ends, axes, steps], outputs=["slice_out"])[0] + + @gs.Graph.register() def nested(self, inp, graph): return self.layer(op="Nested", inputs=[inp], outputs=["nested_out"], attrs={"body": graph})[0] @@ -920,6 +925,33 @@ class TestFoldConstants(object): assert isinstance(graph.outputs[2], Variable) + @pytest.mark.parametrize("shape, starts, ends, axes, steps, expected", [ + (("batch", 3, "height", "width"), 1, 2, 0, 1, [3]), # Scalar starts/ends case + (("batch", 3, "height", "width"), [1], [2], [0], [1], [3]), + (("batch", 3, 5, "width"), [1], [-1], [0], [1], [3, 5]), # Negative ends case + (("batch", 3, 5, 7), [1], [2000], [0], [1], [3, 5, 7]), # Past end, ends case + (("batch", 3, 5, 7), [-2], [4], [0], [1], [5, 7]), # Negative starts case + (("batch", 3, 5, 7), [-2], [4], [1], [1], None), # Non-zero axes case + (("batch", 3, 5, "width"), [-2], [4], [1], [1], None), # Dynamic case + (("batch", 3, 5, 7), [1], [4], [0], [2], [3, 7]), # Non-one steps case + (("batch", 3, 5, 7), [4], [0], [0], [-1], [7, 5, 3]), # Negative steps case + ]) + def test_shape_slice(self, shape, starts, ends, axes, steps, expected): + inp = Variable("input", dtype=np.float32, shape=shape) + graph = Graph(inputs=[inp]) + + inp_shape = graph.shape(inp) + graph.outputs = [graph.slice(inp_shape, np.array(starts), np.array(ends), axes=np.array(axes), steps=np.array(steps))] + + graph.fold_constants() + + if expected: + assert isinstance(graph.outputs[0], Constant) + assert np.all(graph.outputs[0].values == expected) + else: + assert isinstance(graph.outputs[0], Variable) + + def test_with_nested_graph(self): cond = gs.Variable("cond", dtype=np.bool, shape=(1, )) diff --git a/tools/pytorch-quantization/README.md b/tools/pytorch-quantization/README.md index 05c18f24..ffade2ec 100644 --- a/tools/pytorch-quantization/README.md +++ b/tools/pytorch-quantization/README.md @@ -1,6 +1,6 @@ # Pytorch Quantization -PyTorch-Quantization is a toolkit for training and evaluating PyTorch models with simulated quantization. Quantization can be added to the model automatically, or manually, allowing the model to be tuned for accuracy and performance. Quantization is compatible with NVIDIAs high performance integer kernels which leverage integer Tensor Cores. The quantized model can be exported to ONNX and imported to an upcoming version of TensorRT. +PyTorch-Quantization is a toolkit for training and evaluating PyTorch models with simulated quantization. Quantization can be added to the model automatically, or manually, allowing the model to be tuned for accuracy and performance. Quantization is compatible with NVIDIAs high performance integer kernels which leverage integer Tensor Cores. The quantized model can be exported to ONNX and imported by TensorRT 8.0 and later. ## Install @@ -17,14 +17,18 @@ git clone https://github.com/NVIDIA/TensorRT.git cd tools/pytorch-quantization ``` -Install prerequisites +Install PyTorch and prerequisites ```bash pip install -r requirements.txt -pip install torch +# for CUDA 10.2 users +pip install torch>=1.8.0 +# for CUDA 11.1 users +pip install torch>=1.8.0+cu111 ``` Build and install pytorch-quantization ```bash +# Python version >= 3.7, GCC version >= 5.4 required python setup.py install ``` diff --git a/tools/pytorch-quantization/docs/source/tutorials/quant_resnet50.rst b/tools/pytorch-quantization/docs/source/tutorials/quant_resnet50.rst index 4db3bede..b1a3f009 100644 --- a/tools/pytorch-quantization/docs/source/tutorials/quant_resnet50.rst +++ b/tools/pytorch-quantization/docs/source/tutorials/quant_resnet50.rst @@ -177,7 +177,7 @@ We can try different calibrations without recollecting the histograms, and see w MSE and entropy should both get over 76%. 99.9% clips too many values for resnet50 and will get slightly lower accuracy. -Quantized fine tuning +Quantization Aware Training --------------------- Optionally, we can fine-tune the calibrated model to improve accuracy further. @@ -198,3 +198,120 @@ After one epoch of fine-tuning, we can achieve over 76.4% top-1 accuracy. Fine-tuning for more epochs with learning rate annealing can improve accuracy further. For example, fine-tuning for 15 epochs with cosine annealing starting with a learning rate of 0.001 can get over 76.7%. It should be noted that the same fine-tuning schedule will improve the accuracy of the unquantized model as well. + +Further optimization +~~~~~~~~~~~~~~~~~~~~ + +For efficient inference on TensorRT, we need know more details about the runtime optimization. +TensorRT supports fusion of quantizing convolution and residual add. +The new fused operator has two inputs. Let us call them conv-input and residual-input. +Here the fused operator’s output precision must match the residual input precision. +When there is another quantizing node after the fused operator, +we can insert a pair of quantizing/dequantizing nodes between the residual-input and the Elementwise-Addition node, +so that quantizing node after the Convolution node is fused with the Convolution node, and the Convolution node is completely quantized with INT8 input and output. +We cannot use automatic monkey-patching to apply this optimization and we need to manually insert the quantizing/dequantizing nodes. + +First create a copy of resnet.py from https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py, +modify the constructor, add explicit bool flag ‘quantize’ + +.. code:: python + + def resnet50(pretrained: bool = False, progress: bool = True, quantize: bool = False, **kwargs: Any) -> ResNet: + return _resnet('resnet50', Bottleneck, [3, 4, 6, 3], pretrained, progress, quantize, **kwargs) + def _resnet(arch: str, block: Type[Union[BasicBlock, Bottleneck]], layers: List[int], pretrained: bool, progress: bool, + quantize: bool, **kwargs: Any) -> ResNet: + model = ResNet(block, layers, quantize, **kwargs) + class ResNet(nn.Module): + def __init__(self, + block: Type[Union[BasicBlock, Bottleneck]], + layers: List[int], + quantize: bool = False, + num_classes: int = 1000, + zero_init_residual: bool = False, + groups: int = 1, + width_per_group: int = 64, + replace_stride_with_dilation: Optional[List[bool]] = None, + norm_layer: Optional[Callable[..., nn.Module]] = None) -> None: + super(ResNet, self).__init__() + self._quantize = quantize + +When this ``self._quantize`` flag is set to ``True``, we need replace all the ``nn.Conv2d`` with ``quant_nn.QuantConv2d``. + + +.. code:: python + + def conv3x3(in_planes: int, + out_planes: int, + stride: int = 1, + groups: int = 1, + dilation: int = 1, + quantize: bool = False) -> nn.Conv2d: + """3x3 convolution with padding""" + if quantize: + return quant_nn.QuantConv2d(in_planes, + out_planes, + kernel_size=3, + stride=stride, + padding=dilation, + groups=groups, + bias=False, + dilation=dilation) + else: + return nn.Conv2d(in_planes, + out_planes, + kernel_size=3, + stride=stride, + padding=dilation, + groups=groups, + bias=False, + dilation=dilation) + def conv1x1(in_planes: int, out_planes: int, stride: int = 1, quantize: bool = False) -> nn.Conv2d: + """1x1 convolution""" + if quantize: + return quant_nn.QuantConv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False) + else: + return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False) + + +The residual conv add can be find both in both ``BasicBlock`` and ``Bottleneck``. +We need first declare quantization node in the ``__init__`` function. + + +.. code:: python + + def __init__(self, + inplanes: int, + planes: int, + stride: int = 1, + downsample: Optional[nn.Module] = None, + groups: int = 1, + base_width: int = 64, + dilation: int = 1, + norm_layer: Optional[Callable[..., nn.Module]] = None, + quantize: bool = False) -> None: + # other code... + self._quantize = quantize + if self._quantize: + self.residual_quantizer = quant_nn.TensorQuantizer(quant_nn.QuantConv2d.default_quant_desc_input) + + +Finally we need patch the ``forward`` function in both ``BasicBlock`` and ``Bottleneck``, +inserting extra quantization/dequantization nodes here. + + +.. code:: python + + def forward(self, x: Tensor) -> Tensor: + # other code... + if self._quantize: + out += self.residual_quantizer(identity) + else: + out += identity + out = self.relu(out) + + return out + +The final resnet code with residual quantized can be found in https://github.com/NVIDIA/TensorRT/blob/master/tools/pytorch-quantization/examples/torchvision/models/classification/resnet.py + + + diff --git a/tools/pytorch-quantization/docs/source/userguide.rst b/tools/pytorch-quantization/docs/source/userguide.rst index 1ebaa35d..24598afe 100644 --- a/tools/pytorch-quantization/docs/source/userguide.rst +++ b/tools/pytorch-quantization/docs/source/userguide.rst @@ -145,16 +145,16 @@ be used as the following example: # Keep running the quantized model # ... -Quantized Fine Tuning +Quantization Aware Training --------------------- -Quantized fine tuning is based on Straight Through Estimator (STE) +Quantization Aware Training is based on Straight Through Estimator (STE) derivative approximation. It is some time known as “quantization aware training”. We don’t use the name because it doesn’t reflect the underneath assumption. If anything, it makes training being “unaware” of quantization because of the STE approximation. -After calibration is done, quantized fine tuning is simply select a +After calibration is done, Quantization Aware Training is simply select a training schedule and continue training the calibrated model. Usually, it doesn’t need to fine tune very long. We usually use around 10% of the original training schedule, starting at 1% of the initial training @@ -166,7 +166,7 @@ learning rate). Some recommendations ~~~~~~~~~~~~~~~~~~~~ -Quantized fine tuning (Essentially a discrete numerical optimization +Quantization Aware Training (Essentially a discrete numerical optimization problem) is not a solved problem mathematically. Based on our experience, here are some recommendations: diff --git a/tools/pytorch-quantization/examples/calibrate_quant_resnet50.ipynb b/tools/pytorch-quantization/examples/calibrate_quant_resnet50.ipynb index 9315a0f8..bf0c427a 100644 --- a/tools/pytorch-quantization/examples/calibrate_quant_resnet50.ipynb +++ b/tools/pytorch-quantization/examples/calibrate_quant_resnet50.ipynb @@ -10,6 +10,7 @@ "import os\n", "import sys\n", "import time\n", + "import collections\n", "\n", "import torch\n", "import torch.utils.data\n", @@ -527,7 +528,8 @@ "\n", "traindir = os.path.join(data_path, 'train')\n", "valdir = os.path.join(data_path, 'val')\n", - "dataset, dataset_test, train_sampler, test_sampler = load_data(traindir, valdir, False, False)\n", + "_args = collections.namedtuple('mock_args', ['model', 'distributed', 'cache_dataset'])\n", + "dataset, dataset_test, train_sampler, test_sampler = load_data(traindir, valdir, _args(model=model_name, distributed=False, cache_dataset=False))\n", "\n", "data_loader = torch.utils.data.DataLoader(\n", " dataset, batch_size=batch_size,\n", diff --git a/tools/pytorch-quantization/examples/torchvision/classification_flow.py b/tools/pytorch-quantization/examples/torchvision/classification_flow.py index 54cbe1c9..4a4ee379 100644 --- a/tools/pytorch-quantization/examples/torchvision/classification_flow.py +++ b/tools/pytorch-quantization/examples/torchvision/classification_flow.py @@ -20,6 +20,7 @@ import sys import time import argparse import warnings +import collections import torch import torch.utils.data @@ -38,11 +39,11 @@ from pytorch_quantization import quant_modules import onnxruntime import numpy as np -import models +import models from prettytable import PrettyTable -# The following path assumes running in nvcr.io/nvidia/pytorch:20.08-py3 +# The following path assumes running in nvcr.io/nvidia/pytorch:20.08-py3 sys.path.insert(0,"/opt/pytorch/vision/references/classification/") # Import functions from torchvision reference @@ -168,11 +169,13 @@ def prepare_model( ## Prepare the data loaders traindir = os.path.join(data_dir, 'train') valdir = os.path.join(data_dir, 'val') - dataset, dataset_test, train_sampler, test_sampler = load_data(traindir, valdir, False, False) + _args = collections.namedtuple("mock_args", ["model", "distributed", "cache_dataset"]) + dataset, dataset_test, train_sampler, test_sampler = load_data( + traindir, valdir, _args(model=model_name, distributed=False, cache_dataset=False)) data_loader_train = torch.utils.data.DataLoader( dataset, batch_size=batch_size_train, - sampler=train_sampler, num_workers=16, pin_memory=True) + sampler=train_sampler, num_workers=4, pin_memory=True) data_loader_test = torch.utils.data.DataLoader( dataset_test, batch_size=batch_size_test, diff --git a/tools/pytorch-quantization/examples/torchvision/models/classification/resnet.py b/tools/pytorch-quantization/examples/torchvision/models/classification/resnet.py index e9231578..f1c9f9e4 100644 --- a/tools/pytorch-quantization/examples/torchvision/models/classification/resnet.py +++ b/tools/pytorch-quantization/examples/torchvision/models/classification/resnet.py @@ -1,35 +1,3 @@ -# -# BSD 3-Clause License -# -# Copyright (c) Soumith Chintala 2016, -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# * Neither the name of the copyright holder nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - # # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. # @@ -45,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - import torch from torch import Tensor import torch.nn as nn diff --git a/tools/pytorch-quantization/pytorch_quantization/__init__.py b/tools/pytorch-quantization/pytorch_quantization/__init__.py index 0c8b1c76..ca1542d7 100644 --- a/tools/pytorch-quantization/pytorch_quantization/__init__.py +++ b/tools/pytorch-quantization/pytorch_quantization/__init__.py @@ -16,4 +16,5 @@ from absl import logging +from .version import __version__ logging.use_absl_handler() diff --git a/tools/pytorch-quantization/pytorch_quantization/nn/modules/quant_rnn.py b/tools/pytorch-quantization/pytorch_quantization/nn/modules/quant_rnn.py index e6d53f34..12ccdc85 100644 --- a/tools/pytorch-quantization/pytorch_quantization/nn/modules/quant_rnn.py +++ b/tools/pytorch-quantization/pytorch_quantization/nn/modules/quant_rnn.py @@ -42,7 +42,7 @@ class QuantRNNBase(nn.Module, _utils.QuantMixin): def __init__(self, mode, input_size, hidden_size, num_layers=1, bias=True, batch_first=False, - dropout=0, bidirectional=False, **kwargs): + dropout=0, bidirectional=False, proj_size=0, **kwargs): super(QuantRNNBase, self).__init__() self.mode = mode self.input_size = input_size @@ -53,6 +53,7 @@ class QuantRNNBase(nn.Module, _utils.QuantMixin): self.dropout = dropout self.dropout_state = {} self.bidirectional = bidirectional + self.proj_size = proj_size num_directions = 2 if bidirectional else 1 if not isinstance(dropout, numbers.Number) or not 0 <= dropout <= 1 or \ @@ -66,6 +67,11 @@ class QuantRNNBase(nn.Module, _utils.QuantMixin): "num_layers greater than 1, but got dropout={} and " "num_layers={}".format(dropout, num_layers)) + if proj_size < 0: + raise ValueError("proj_size should be a positive integer or zero to disable projections") + if proj_size > 0: + raise ValueError("proj_size is not supported in pytorch-quantization yet") + if mode == 'LSTM': gate_size = 4 * hidden_size elif mode == 'GRU': @@ -131,10 +137,10 @@ class QuantRNNBase(nn.Module, _utils.QuantMixin): with torch.no_grad(): # NB: this is an INPLACE function on weight_arr, that's why the # no_grad() is necessary. - weight_buf = torch._cudnn_rnn_flatten_weight( - weight_arr, weight_stride0, - self.input_size, rnn.get_cudnn_mode(self.mode), self.hidden_size, self.num_layers, - self.batch_first, bool(self.bidirectional)) + weight_buf = torch._cudnn_rnn_flatten_weight(weight_arr, weight_stride0, self.input_size, + rnn.get_cudnn_mode(self.mode), self.hidden_size, + self.proj_size, self.num_layers, self.batch_first, + bool(self.bidirectional)) self._param_buf_size = weight_buf.size(0) self._data_ptrs = list(p.data.data_ptr() for p in self.parameters()) @@ -272,6 +278,8 @@ class QuantRNN(QuantRNNBase): """ def __init__(self, *args, **kwargs): + if 'proj_size' in kwargs: + raise ValueError("proj_size argument is only supported for LSTM, not RNN or GRU") if 'nonlinearity' in kwargs: if kwargs['nonlinearity'] == 'tanh': mode = 'RNN_TANH' diff --git a/tools/pytorch-quantization/setup.py b/tools/pytorch-quantization/setup.py index 7af6b8b5..674e362a 100644 --- a/tools/pytorch-quantization/setup.py +++ b/tools/pytorch-quantization/setup.py @@ -74,5 +74,5 @@ setup( url="https://github.com/nvidia/tensorrt/tools/pytorch-quantization", author="NVIDIA", author_email="nvidia@nvidia.com", - + license="Apache 2.0", ) diff --git a/tools/pytorch-quantization/tests/classification_flow_test.py b/tools/pytorch-quantization/tests/classification_flow_test.py index c718adae..c1bb4656 100644 --- a/tools/pytorch-quantization/tests/classification_flow_test.py +++ b/tools/pytorch-quantization/tests/classification_flow_test.py @@ -26,7 +26,7 @@ import pytest class TestClassificationFlow(): - def test_resnet50(self, request, pytestconfig): + def test_resnet18(self, request, pytestconfig): dir_path = os.path.dirname(os.path.realpath(__file__)) dataset_dir = pytestconfig.getoption('--data-dir') @@ -56,7 +56,7 @@ class TestClassificationFlow(): [ 'python3', dir_path + '/../examples/torchvision/classification_flow.py', '--data-dir', dataset_dir, - '--model', 'resnet50', '--pretrained', + '--model', 'resnet18', '--pretrained', '-t', '0.5', '--num-finetune-epochs', '1', '--evaluate-onnx',