使用TensorRT加速深度学习推理


官方链接

入门博客
开发者指南
代码示例
github
预训练模型

知乎

高性能深度学习支持引擎实战——TensorRT
TensorRT部署深度学习模型
深度学习算法优化系列十七 | TensorRT介绍,安装及如何使用?


前言

NVIDIA TensorRT是用于深度学习推理的SDK。TensorRT提供API和解析器以从所有主要的深度学习框架中导入经过训练的模型。然后,它会生成可部署在数据中心以及汽车和嵌入式环境中的优化运行时引擎。

这篇文章提供了使用TensorRT的简单介绍。您将学习如何在GPU上部署深度学习应用程序,增加吞吐量并减少推理期间的延迟。它使用C ++示例引导你完成将PyTorch模型转换为ONNX模型并将其导入TensorRT,应用优化并为数据中心环境生成高性能运行时引擎的过程。

TensorRT支持C ++和Python;如果你使用其中任何一个,则此工作流程讨论可能会很有用。如果你喜欢使用Python,请参阅TensorRT文档中的使用Python API。

深度学习适用于广泛的应用程序,例如自然语言处理,推荐系统,图像和视频分析。随着越来越多的应用在生产中使用深度学习,对准确性和性能的要求导致模型复杂性和尺寸的强劲增长。

汽车等安全性至关重要的应用对深度学习模型期望的吞吐量和延迟提出了严格要求。对于某些消费者应用程序(包括推荐系统)也是如此。

TensorRT旨在帮助针对这些用例部署深度学习。借助对每个主要框架的支持,TensorRT通过强大的优化,降低的精度使用和有效的内存使用,帮助以低延迟处理大量数据。

要继续阅读本文,你需要一台具有CUDA功能的GPU的计算机或具有GPU的云实例并安装TensorRT。在Linux上,最容易上手的地方是从NVIDIA NGC容器注册表中下载具有TensorRT集成的GPU加速的PyTorch容器。

该示例应用程序使用来自Kaggle的Brain MRI分割数据的输入数据 来执行推理。

简单的TensorRT示例

以下是此示例应用程序的四个步骤:

将预训练的图像分割PyTorch模型转换为ONNX。

  1. 将ONNX模型导入TensorRT
  2. 应用优化并生成引擎
  3. 在GPU上执行推理
  4. 导入ONNX模型包括从磁盘上的已保存文件加载它,并将其从其本机框架或格式转换为TensorRT网络。ONNX是用于表示深度学习模型的标准,使它们可以在框架之间转移。

Caffe2,Chainer,CNTK,PaddlePaddle,PyTorch和MXNet等许多框架都支持ONNX格式。接下来,基于输入模型,目标GPU平台和指定的其他配置参数构建优化的TensorRT引擎。最后一步是向TensorRT引擎提供输入数据以执行推理。

该应用程序在TensorRT中使用以下组件:

ONNX parser:将经过PyTorch训练的模型转换为ONNX格式作为输入,并在TensorRT中填充网络对象。
Builder:在TensorRT中使用网络并生成针对目标平台进行了优化的引擎。
Engine:获取输入数据,执行推理并发出推理输出。
Logger:与构建器和引擎相关联,以在构建和推断阶段捕获错误,警告和其他信息。

将预先训练的图像分割PyTorch模型转换为ONNX

从NGC注册表中的PyTorch容器开始,以获取预安装的框架和CUDA组件并准备就绪。成功安装PyTorch容器后,运行以下命令以下载运行此示例应用程序所需的所有内容(示例代码,测试输入数据和参考输出),更新依赖项并使用提供的makefile编译应用程序。

>> sudo apt-get install libprotobuf-dev protobuf-compiler # protobuf is a prerequisite library
>> git clone --recursive https://github.com/onnx/onnx.git # Pull the ONNX repository from GitHub 
>> cd onnx
>> mkdir build && cd build 
>> cmake .. # Compile and install ONNX
>> make # Use the ‘-j’ option for parallel jobs, for example, ‘make -j $(nproc)’ 
>> make install 
>> cd ../..
>> git clone https://github.com/parallel-forall/code-samples.git
>> cd code-samples/posts/TensorRT-introduction
>> make clean && make # Compile the TensorRT C++ code
>> cd ..
>> wget https://developer.download.nvidia.com/devblogs/speeding-up-unet.7z // Get the ONNX model and test the data
>> tar xvf speeding-up-unet.7z # Unpack the model data into the unet folder
>> cd unet
>> python create_network.py #Inside the unet folder, it creates the unet.onnx file

将经过PyTorch训练的UNet模型转换为ONNX,如以下代码示例所示:

import torch
from torch.autograd import Variable
import torch.onnx as torch_onnx
import onnx
def main():
    input_shape = (3, 256, 256)
    model_onnx_path = "unet.onnx"
    dummy_input = Variable(torch.randn(1, *input_shape))
    model = torch.hub.load('mateuszbuda/brain-segmentation-pytorch', 'unet',
      in_channels=3, out_channels=1, init_features=32, pretrained=True)
    model.train(False)
    
    inputs = ['input.1']
    outputs = ['186']
    dynamic_axes = {'input.1': {0: 'batch'}, '186':{0:'batch'}}
    out = torch.onnx.export(model, dummy_input, model_onnx_path, input_names=inputs, output_names=outputs, dynamic_axes=dynamic_axes)

if __name__=='__main__':
    main() 

接下来,为推理准备输入数据。从Kaggle目录下载所有图片。将文件名中没有_mask的三张图片和brain-segmentation-pytorch存储库中的utils.py文件复制到/unet目录。准备三张图片,稍后在这篇文章中用作输入数据。准备input_0。pb和ouput_0。pb文件以后使用,运行以下代码示例:

import torch 
import argparse
import numpy as np
from torchvision import transforms                    
from skimage.io import imread
from onnx import numpy_helper
from utils import normalize_volume
def main(args):
    model = torch.hub.load('mateuszbuda/brain-segmentation-pytorch', 'unet',
      in_channels=3, out_channels=1, init_features=32, pretrained=True)
    model.train(False)
    
    filename = args.input_image
    input_image = imread(filename)
    input_image = normalize_volume(input_image)
    input_image = np.asarray(input_image, dtype='float32')
    
    preprocess = transforms.Compose([
      transforms.ToTensor(),
    ])
    input_tensor = preprocess(input_image)
    input_batch = input_tensor.unsqueeze(0)
    
    tensor1 = numpy_helper.from_array(input_batch.numpy())
    with open(args.input_tensor, 'wb') as f:
        f.write(tensor1.SerializeToString())
    if torch.cuda.is_available():
        input_batch = input_batch.to('cuda')
        model = model.to('cuda')
    with torch.no_grad():
        output = model(input_batch)
    
    tensor = numpy_helper.from_array(output[0].cpu().numpy())
    with open(args.output_tensor, 'wb') as f:
        f.write(tensor.SerializeToString())
if __name__=='__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--input_image', type=str)
    parser.add_argument('--input_tensor', type=str, default='input_0.pb')
    parser.add_argument('--output_tensor', type=str, default='output_0.pb')
    args=parser.parse_args()
    main(args) 

运行以下命令,要生成经过处理的输入数据进行推理:

>> pip install medpy #dependency for utils.py file
>> mkdir test_data_set_0
>> mkdir test_data_set_1
>> mkdir test_data_set_2
>> python prepareData.py --input_image your_image1 --input_tensor test_data_set_0/input_0.pb --output_tensor test_data_set_0/output_0.pb   # This creates input_0.pb and output_0.pb
>> python prepareData.py --input_image your_image2 --input_tensor test_data_set_1/input_0.pb --output_tensor test_data_set_1/output_0.pb   # This creates input_0.pb and output_0.pb
>> python prepareData.py --input_image your_image3 --input_tensor test_data_set_2/input_0.pb --output_tensor test_data_set_2/output_0.pb   # This creates input_0.pb and output_0.pb 

就这样,你就有了可以执行推断的输入数据。从应用程序的简化版本simpleONNX_1.cpp开始,并在其上进行构建。后续版本在同一个文件夹simpleONNX_2.cpp和simpleONNX.cpp中可用。

将ONNX模型导入TensorRT,生成引擎,并执行推理

使用训练有素的模型运行示例应用程序,并将输入数据作为输入传递。数据以ONNX protobuf文件的形式提供。该示例应用程序将TensorRT生成的输出与同一文件夹中ONNX .pb文件中可用的参考值进行比较,并在提示符下汇总结果。

导入UNet ONNX模型并生成引擎可能需要几秒钟。它还将以便携式灰度图(PGM)格式生成输出图像,作为output.pgm。

>> cd to code-samples/posts/TensorRT-introduction
>> ./simpleOnnx_1 path/to/unet/unet.onnx path/to/unet/test_data_set_0/input_0.pb # The sample application expects output reference values in path/to/unet/test_data_set_0/output_0.pb
...
 Tactic: 0 is the only option, timing skipped
: Fastest Tactic: 0 Time: 0
: Formats and tactics selection completed in 2.26589 seconds.
: After reformat layers: 32 layers
: Block size 1073741824
: Block size 536870912

...
: Total Activation Memory: 2248146944
INFO: Detected 1 inputs and 1 output network tensors.
Engine generation completed in 3.37261 seconds.
OK 

就是这样,你拥有一个使用TensorRT优化并在GPU上运行的应用程序。图2显示了一个示例测试用例的输出。

在大脑MRI图像上使用张排序推理

这是早期示例应用程序中使用的一些关键代码示例。

以下代码示例中的主要功能首先声明一个CUDA引擎来保存网络定义和训练后的参数。引擎是通过createCudaEngine将ONNX模型的路径作为输入的函数生成的。

// Declare the CUDA engineunique_ptr<ICudaEngine, Destroy<ICudaEngine>> engine{nullptr};
...
// Create the CUDA engine
engine.reset(createCudaEngine(onnxModelPath));

createCudaEngine函数解析ONNX模型并将其保存在网络对象中。要处理U-Net模型的输入图像和形状张量的动态输入尺寸,必须从builder类创建优化配置文件,如以下代码示例所示。

通过优化配置文件,你可以为配置文件设置最佳输入,最小和最大尺寸。构建器选择内核,以使输入张量尺寸的运行时间最少,并且对于最小尺寸和最大尺寸之间的所有输入张量尺寸均有效。还将网络对象转换为TensorRT引擎。

setMaxBatchSize以下代码示例中的函数用于指定TensorRT引擎期望的最大批处理大小。该setMaxWorkspaceSize功能使你可以在引擎构建阶段增加GPU内存占用。

nvinfer1::ICudaEngine* createCudaEngine(string const& onnxModelPath, int batchSize){
    unique_ptr<nvinfer1::IBuilder, Destroy<nvinfer1::IBuilder>> builder{nvinfer1::createInferBuilder(gLogger)};
    const auto explicitBatch = 1U << static_cast<uint32_t>(nvinfer1::NetworkDefinitionCreationFlag::kEXPLICIT_BATCH);
    unique_ptr<nvinfer1::INetworkDefinition, Destroy<nvinfer1::INetworkDefinition>> network{builder->createNetworkV2(explicitBatch)};
    unique_ptr<nvonnxparser::IParser, Destroy<nvonnxparser::IParser>> parser{nvonnxparser::createParser(*network, gLogger)};
    unique_ptr<nvinfer1::IBuilderConfig,Destroy<nvinfer1::IBuilderConfig>> config{builder->createBuilderConfig()};
 
    if (!parser->parseFromFile(onnxModelPath.c_str(), static_cast<int>(ILogger::Severity::kINFO)))
    {
            cout << "ERROR: could not parse input engine." << endl;
            return nullptr;
    }
    builder->setMaxBatchSize(batchSize);
    config->setMaxWorkspaceSize((1 << 30));
    
    auto profile = builder->createOptimizationProfile();
    profile->setDimensions(network->getInput(0)->getName(), OptProfileSelector::kMIN, Dims4{1, 3, 256 , 256});
    profile->setDimensions(network->getInput(0)->getName(), OptProfileSelector::kOPT, Dims4{1, 3, 256 , 256});
    profile->setDimensions(network->getInput(0)->getName(), OptProfileSelector::kMAX, Dims4{32, 3, 256 , 256});    
    config->addOptimizationProfile(profile);
    return builder->buildEngineWithConfig(*network, *config);
} 

创建引擎后,创建执行上下文以保存推理期间生成的中间激活值。以下代码显示了如何创建执行上下文。

// Declare the execution context
unique_ptr<IExecutionContext, Destroy<IExecutionContext>> context{nullptr};
...
// Create the execution context
context.reset(engine->createExecutionContext()); 

此应用程序launchInference通过以下代码示例中所示的函数将推理请求异步地放置在GPU上。将输入从主机(CPU)复制到其中的设备(GPU)launchInference,然后使用该enqueue函数进行推断,然后异步复制回结果。

该示例使用CUDA流来管理GPU上的异步工作。异步推理执行通常通过重叠计算来提高性能,因为它可以最大化GPU利用率。该enqueue函数将推理请求放在CUDA流上,并以输入运行时批大小,输入和输出的指针以及要用于内核执行的CUDA流作为输入。从主机到设备执行异步数据传输,然后使用反向传输cudaMemcpyAsync

void launchInference(IExecutionContext* context, cudaStream_t stream, vector<float> const& inputTensor, vector<float>& outputTensor, void** bindings, int batchSize)
{
    int inputId = getBindingInputIndex(context);

    cudaMemcpyAsync(bindings[inputId], inputTensor.data(), inputTensor.size() * sizeof(float), cudaMemcpyHostToDevice, stream);
    context->enqueueV2(bindings, stream, nullptr);
    cudaMemcpyAsync(outputTensor.data(), bindings[1 - inputId], outputTensor.size() * sizeof(float), cudaMemcpyDeviceToHost, stream);
} 

cudaStreamSynchronize调用后使用该函数launchInference可确保在访问结果之前完成GPU计算。可以使用ICudaEngine类中的函数查询输入和输出的数量,以及每个输入和输出的尺寸。该示例最终将参考输出与TensorRT生成的推论进行比较,并将差异打印到提示中。

有关类的更多信息,请参见TensorRT类列表。完整的代码示例在simpleOnnx_1.cpp中。

批量输入

该应用示例期望单个输入并在对其进行推断之后返回输出。实际应用程序通常批量输入以实现更高的性能和效率。可以在神经网络的不同层上并行计算一批形状和大小相同的输入。

较大的批次通常可以更有效地利用GPU资源。例如,在Volta和Turing GPU上,使用32的倍数进行批量处理可能会特别快速高效,但精度较低,因为TensorRT可以将特殊内核用于矩阵乘法和利用Tensor核心的完全连接层。

使用以下代码在命令行上将图像传递给应用程序。在此示例中,在命令行中作为输入参数传递的图像(.pb文件)的数量决定了批处理大小。使用test_data_set_ *从所有目录中获取所有input_0.pb文件。以下命令不仅读取一个输入,还读取文件夹中所有可用的输入。

当前,下载的数据具有三个输入目录,因此批处理大小为3。此版本的示例对应用程序进行了配置,并将结果打印到提示中。有关更多信息,请参见下一节“分析应用程序”。

>> ./simpleOnnx_2 path/to/unet.onnx path/to/unet/test_data_set_*/input_0.pb # Use all available test data sets.
...
: Formats and tactics selection completed in 2.33156 seconds.
: After reformat layers: 32 layers
: Block size 1073741824
: Block size 536870912
...
: Total Activation Memory: 2248146944
INFO: Detected 1 inputs and 1 output network tensors.
: Engine generation completed in 3.45499 seconds.
Inference batch size 3 average over 10 runs is 5.23616ms
OK 

要在一次推理中处理多个图像,请对应用程序进行一些更改。首先,循环收集所有图像(.pb文件)以用作应用程序中的输入:

input_files.push_back(string{argv[2]});    
for (int i = 2; i < argc; ++i)
   input_files.push_back(string{argv[i]}); 

接下来,使用该setMaxBatchSize函数指定TensorRT引擎期望的最大批处理大小。然后,构建器将生成一个针对该批处理量调整的引擎,方法是选择算法以使其在目标平台上的性能最大化。尽管引擎不接受较大的批次大小,但允许在运行时使用较小的批次大小。

maxBatchSize值的选择取决于应用程序以及任何给定时间的预期推理流量(例如,图像数量)。一种常见的做法是构建针对不同批次大小优化的多个引擎(使用不同的maxBatchSize值),然后在运行时选择最优化的引擎。

如果未指定,则默认批处理大小为1,这意味着引擎不会处理大于1的批处理大小。如以下代码示例所示,设置此参数:

 builder-> setMaxBatchSize(batchSize); 

剖析应用程序

现在,你已经看到了一个示例,下面是如何衡量其性能。用于网络推断的最简单的性能度量是输入到网络的输入到输出返回的时间,称为等待时间。

对于嵌入式平台上的许多应用程序,延迟是至关重要的,而消费者应用程序则需要服务质量。较低的延迟使这些应用程序更好。本示例使用GPU上的时间戳来衡量应用程序的平均延迟。有多种方法可以在CUDA中分析你的应用程序。有关更多信息,请参见如何在CUDA C / C ++中实现性能指标。

CUDA提供了轻量级的事件API函数来创建,销毁和记录事件,以及计算事件之间的时间。该应用程序可以在CUDA流中记录事件,一个在启动推理之前记录,另一个在推理完成之后记录,如以下代码示例所示。

在某些情况下,你可能需要考虑在推理开始之前和推理完成之后在GPU和CPU之间传输数据所花费的时间。存在将数据预取到GPU以及与数据传输进行重叠计算的技术,这些技术可以显着隐藏数据传输开销。该函数cudaEventElapsedTime测量CUDA流中遇到这两个事件之间的时间。

使用上一节开头的代码示例来运行此示例并查看分析输出。要分析应用程序,请将推理启动包装doInference在simpleONNX_2.cpp中的函数中。此示例包括一个更新的函数调用。

launchInference(context, stream, inputTensor, outputTensor, bindings, batchSize);
//Wait until the work is finished
cudaStreamSynchronize(stream);
doInference(context.get(), stream, inputTensor, outputTensor, bindings, batchSize); 

计算延迟的范围doInference如下:

// Number of times to run inference and calculate average timeconstexpr int ITERATIONS = 10;
...
void doInference(IExecutionContext* context, cudaStream_t stream, vector<float> const& inputTensor, vector<float>& outputTensor, void** bindings, int batchSize)
{
    CudaEvent start;
    CudaEvent end;
    double totalTime = 0.0;

    for (int i = 0; i < ITERATIONS; ++i)
    {
        float elapsedTime;

        // Measure time that it takes to copy input to GPU, run inference, and move output back to CPU
        cudaEventRecord(start, stream);
        launchInference(context, stream, inputTensor, outputTensor, bindings, batchSize);
        cudaEventRecord(end, stream);

        // Wait until the work is finished
        cudaStreamSynchronize(stream);
        cudaEventElapsedTime(&elapsedTime, start, end);

        totalTime += elapsedTime;
    } 

    cout << "Inference batch size " << batchSize << " average over " << ITERATIONS << " runs is " << totalTime / ITERATIONS << "ms" << endl;
} 

许多应用程序对积累和批处理的大量输入数据进行推理,以进行脱机处理。每秒可能的最大推理数量(称为吞吐量)是这些应用程序的重要指标。

你可以通过为更大的特定批次大小生成优化的引擎,运行推断并测量每秒可以处理的批次数量来测量吞吐量。使用每秒的批处理数量和批处理大小来计算每秒的推理数量,但这超出了本文的范围。

优化你的应用

既然你知道如何批量运行推理并分析应用程序,请对其进行优化。TensorRT的关键优势在于它的灵活性和技术的使用,包括混合精度,在所有GPU平台上的高效优化以及对多种模型类型进行优化的能力。

在本节中,我们描述了一些增加吞吐量和减少应用程序延迟的技术。有关更多信息,请参见TensorRT性能的最佳实践。

以下是一些常用技术:

  • 使用混合精度计算
  • 更改工作区大小
  • 重用TensorRT引擎

使用混合精度计算

TensorRT使用FP32算法执行推理,默认情况下可获得最高的推理精度。但是,在许多情况下,你可以使用FP16和INT8精度进行推断,而对结果的准确性影响最小。

使用降低的精度表示模型可以使你在内存中容纳较大的模型,并在降低数据传输要求以降低精度的情况下实现更高的性能。你还可以将计算与FP32和FP16精度与TensorRT混合,称为混合精度,或者将INT8量化精度用于权重,激活和执行图层。

通过setFp16Mode为支持快速FP16数学的设备将参数设置为true来启用FP16内核。

 builder-> setFp16Mode(builder-> platformHasFastFp16());

该setFp16Mode参数向构建者指示较低的精度对于计算是可接受的。如果TensorRT在选择的配置和目标平台上性能更好,则使用FP16优化的内核。

启用此模式后,可以在FP16或FP32中指定权重,并将权重自动转换为适当的精度以进行计算。你还可以灵活地为输入和输出张量指定16位浮点数据类型,这超出了本文的范围。

更改工作区大小

TensorRT允许你使用该setMaxWorkspaceSize功能在引擎构建阶段增加GPU内存占用。增加限制可能会影响可以同时共享GPU的应用程序数量。将此限制设置得太低可能会滤除几种算法并创建次优引擎。即使设置的数量IBuilder::setMaxWorkspaceSize高很多,TensorRT也会仅分配所需的内存。因此,应用程序应允许TensorRT构建器提供尽可能多的工作空间。TensorRT的分配不超过此数量,通常更少。

本示例使用1 GB,这使TensorRT可以选择任何可用的算法。

// Allow TensorRT to use up to 1 GB of GPU memory for tactic selectionconstexpr size_t MAX_WORKSPACE_SIZE = 1ULL << 30; // 1 GB worked well for this example
...
// Set the builder flag
builder->setMaxWorkspaceSize(MAX_WORKSPACE_SIZE); 

重用TensorRT引擎

构建引擎时,构建器对象会为所选平台和配置选择最优化的内核。从网络定义文件构建引擎可能很耗时,除非你更改模型,平台或配置,否则每次执行推理都不应重复进行。

图3显示了生成后可以转换引擎的格式并将其存储在磁盘上以供以后重用,这称为序列化引擎。当你将引擎从磁盘加载到内存中并继续用于推理时,就会发生反序列化。

在这里插入图片描述
运行时对象反序列化引擎。

simpleOnnx.cpp包含每次getCudaEngine加载和使用引擎(如果存在)的功能,而不是每次都创建引擎。如果引擎不可用,它将创建引擎并将其保存在当前目录中,名称为unet_batch4.engine。在本示例尝试构建新引擎之前,如果该引擎在当前目录中可用,它将选择该引擎。

要强制使用更新的配置和参数来构建新引擎,请在重新运行代码示例之前,使用make clean_engines命令删除磁盘上存储的所有现有序列化引擎。

engine.reset(createCudaEngine(onnxModelPath, batchSize))engine.reset(getCudaEngine(onnxModelPath, batchSize)); 
 
ICudaEngine* getCudaEngine(string const& onnxModelPath)
{
    string enginePath{getBasename(onnxModelPath) + ".engine"};
    ICudaEngine* engine{nullptr};

    string buffer = readBuffer(enginePath);
    if (buffer.size())
    {
        // Try to deserialize the engine
        unique_ptr<IRuntime, Destroy<IRuntime>> runtime{createInferRuntime(gLogger)};
        engine = runtime->deserializeCudaEngine(buffer.data(), buffer.size(), nullptr);
    }

    if (!engine)
    {
        // Fall back to creating the engine from scratch
        engine = createCudaEngine(onnxModelPath);

        if (engine)
        {
            unique_ptr<IHostMemory, Destroy<IHostMemory>> engine_plan{engine->serialize()};
            // Try to save the engine for future uses
            writeBuffer(engine_plan->data(), engine_plan->size(), enginePath);
        }
    }
    return engine;
} 

使用此保存的引擎具有不同的批处理大小。下面的代码示例获取输入数据,将其重复执行与批量大小变量相同的次数,然后将此附加的输入传递给样本。第一次运行创建引擎,第二次运行尝试反序列化引擎。

>> for x in seq {1..4} ; do echo path/to/unet/test_data_set_0/input_0.pb ; done  | xargs ./simpleOnnx path/to/unet/unet.onnx...

: Tactic: 0 is the only option, timing skipped
: Fastest Tactic: 0 Time: 0
: Formats and tactics selection completed in 2.3837 seconds.
: After reformat layers: 32 layers
: Block size 1073741824
: Block size 536870912
...
: Total Activation Memory: 2248146944
Inference batch size 4 average over 10 runs is 6.86188ms

>> for x in seq {1 4}; do echo unet/test_data_set_0/input_0.pb ; done  | xargs ./simpleOnnx unet/unet.onnx
: Deserialize required 1400284 microseconds.
Inference batch size 4 average over 10 runs is 6.80197ms
OK 

你现在已经了解了如何使用TensorRT加速简单应用程序的推理。我们使用TensorRT 7测量了NVIDIA TITAN V GPU的早期性能。

  • 2
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值