4. 使用 GPU 进行预测¶
注意:
Config 默认使用 CPU 进行预测,需要通过
EnableUseGpu
来启用 GPU 预测可以尝试启用 CUDNN 和 TensorRT 进行 GPU 预测加速
4.1. GPU 设置¶
API定义如下:
// 启用 GPU 进行预测
// 参数:memory_pool_init_size_mb - 初始化分配的gpu显存,以MB为单位
// device_id - 设备id
// precision - 指定推理精度
// 返回:None
void EnableUseGpu(uint64_t memory_pool_init_size_mb, int device_id = 0, Precision precision = Precision::kFloat32);
// 禁用 GPU 进行预测
// 参数:None
// 返回:None
void DisableGpu();
// 判断是否启用 GPU
// 参数:None
// 返回:bool - 是否启用 GPU
bool use_gpu() const;
// 获取 GPU 的device id
// 参数:None
// 返回:int - GPU 的device id
int gpu_device_id() const;
// 获取 GPU 的初始显存大小
// 参数:None
// 返回:int - GPU 的初始的显存大小
int memory_pool_init_size_mb() const;
// 初始化显存占总显存的百分比
// 参数:None
// 返回:float - 初始的显存占总显存的百分比
float fraction_of_gpu_memory_for_pool() const;
// 开启线程流,目前的行为是为每一个线程绑定一个流,在将来该行为可能改变
// 参数:None
// 返回:None
void EnableGpuMultiStream();
// 判断是否开启线程流
// 参数:None
// 返回:bool - 是否是否开启线程流
bool thread_local_stream_enabled() const;
// 低精度模式(float16)下,推理时期望直接 feed/fetch 低精度数据
// 参数:bool - 是否 feed/fetch 低精度数据
// 返回:None
void EnableLowPrecisionIO(bool x = true);
GPU设置代码示例:
// 创建默认 Config 对象
paddle_infer::Config config;
// 启用 GPU 进行预测 - 初始化 GPU 显存 100M, Deivce_ID 为 0
config.EnableUseGpu(100, 0);
// 通过 API 获取 GPU 信息
std::cout << "Use GPU is: " << config.use_gpu() << std::endl; // true
std::cout << "Init mem size is: " << config.memory_pool_init_size_mb() << std::endl;
std::cout << "Init mem frac is: " << config.fraction_of_gpu_memory_for_pool() << std::endl;
std::cout << "GPU device id is: " << config.gpu_device_id() << std::endl;
// 禁用 GPU 进行预测
config.DisableGpu();
// 通过 API 获取 GPU 信息
std::cout << "Use GPU is: " << config.use_gpu() << std::endl; // false
开启多线程流代码示例:
// 自定义 Barrier 类,用于线程间同步
class Barrier {
public:
explicit Barrier(std::size_t count) : _count(count) {}
void Wait() {
std::unique_lock<std::mutex> lock(_mutex);
if (--_count) {
_cv.wait(lock, [this] { return _count == 0; });
} else {
_cv.notify_all();
}
}
private:
std::mutex _mutex;
std::condition_variable _cv;
std::size_t _count;
};
int test_main(const paddle_infer::Config& config, Barrier* barrier = nullptr) {
static std::mutex mutex;
// 创建 Predictor 对象
std::shared_ptr<paddle_infer::Predictor> predictor;
{
std::unique_lock<std::mutex> lock(mutex);
predictor = std::move(paddle_infer::CreatePredictor(config));
}
if (barrier) {
barrier->Wait();
}
// 准备输入数据
int input_num = shape_production(INPUT_SHAPE);
std::vector<float> input_data(input_num, 1);
auto input_names = predictor->GetInputNames();
auto input_tensor = predictor->GetInputHandle(input_names[0]);
input_tensor->Reshape(INPUT_SHAPE);
input_tensor->CopyFromCpu(input_data.data());
// 执行预测
predictor->Run();
// 获取预测输出
auto output_names = predictor->GetOutputNames();
auto output_tensor = predictor->GetOutputHandle(output_names[0]);
std::vector<int> output_shape = output_tensor->shape();
std::cout << "Output shape is " << shape_to_string(output_shape) << std::endl;
}
int main(int argc, char **argv) {
const size_t thread_num = 5;
std::vector<std::thread> threads(thread_num);
Barrier barrier(thread_num);
// 创建 5 个线程,并为每个线程开启一个单独的 GPU Stream
for (size_t i = 0; i < threads.size(); ++i) {
threads[i] = std::thread([&barrier, i]() {
paddle_infer::Config config;
config.EnableUseGpu(100, 0);
config.SetModel("./model/resnet.pdmodel", "./model/resnet.pdiparams");
config.EnableGpuMultiStream();
test_main(config, &barrier);
});
}
for (auto& th : threads) {
th.join();
}
}
4.2. TensorRT 设置¶
注意:
启用 TensorRT 的前提为已经启用 GPU,否则启用 TensorRT 无法生效
对存在LoD信息的模型,如BERT, ERNIE等NLP模型,必须使用动态 Shape
启用 TensorRT OSS 可以支持更多 plugin,详细参考 TensorRT OSS。当前开始OSS只对ERNIE/BERT模型加速效果(示例代码)。
更多 TensorRT 详细信息,请参考 使用Paddle-TensorRT库预测。
API定义如下:
// 启用 TensorRT 进行预测加速
// 参数:workspace_size - 指定 TensorRT 在网络编译阶段进行kernel选择时使用的工作空间大小,不影响运
// 行时显存占用。该值设置过小可能会导致选不到最佳kernel,设置过大时会增加初始
// 化阶段的显存使用,请根据实际情况调整,建议值256MB
// max_batch_size - 设置最大的 batch 大小,运行时 batch 大小不得超过此限定值
// min_subgraph_size - Paddle 内 TensorRT 是以子图的形式运行,为了避免性能损失,当 TensorRT
// 子图内部节点个数大于 min_subgraph_size 的时候,才会使用 TensorRT 运行
// precision - 指定使用 TensorRT 的精度,支持 FP32(kFloat32),FP16(kHalf),
// Int8(kInt8)
// use_static - 若指定为 true,在初次运行程序退出Predictor析构的时候会将 TensorRT 的优
// 化信息进行序列化到磁盘上。下次运行时直接加载优化的序列化信息而不需要重新生
// 成,以加速启动时间(需要在同样的硬件和相同 TensorRT 版本的情况下)
// use_calib_mode - 若要运行 TensorRT INT8 离线量化校准,需要将此选项设置为 true
// 返回:None
void EnableTensorRtEngine(int workspace_size = 1 << 20,
int max_batch_size = 1, int min_subgraph_size = 3,
Precision precision = Precision::kFloat32,
bool use_static = false,
bool use_calib_mode = true);
// 判断是否启用 TensorRT
// 参数:None
// 返回:bool - 是否启用 TensorRT
bool tensorrt_engine_enabled() const;
// 启用 TensorRT 显存优化
// 参数:engine_memory_sharing - 指定是否开启 TensorRT 显存优化,默认为 false,开启后,当一个模型中(predictor)存在
// 多个 TensorRT Engine(子图)时,它们的运行时 context memory 会共享
// sharing_identifier - 如果有多个可以保证串行执行的模型(多个串行执行的 predictor),可通过此参数控制它们之间的
// 子图共享显存。参与显存共享的 predictor 的 config 配置中,指定此参数为相同的大于0的一个值即可
// 返回:None
void EnableTensorRTMemoryOptim(bool engine_memory_sharing = true,
int sharing_identifier = 0);
// 设置 TensorRT 的动态 Shape
// 参数:min_input_shape - TensorRT 子图支持动态 shape 的最小 shape,推理时输入 shape 的任何
// 维度均不能小于该项配置
// max_input_shape - TensorRT 子图支持动态 shape 的最大 shape,推理是输入 shape 的任何
// 维度均不能大于该项配置
// optim_input_shape - TensorRT 子图支持动态 shape 的最优 shape,TensorRT 在初始化选
// kernel 阶段以此项配置的 shape 下的性能表现作为选择依据
// disable_trt_plugin_fp16 - 设置 TensorRT 的 plugin 不在 fp16 精度下运行
// 返回:None
void SetTRTDynamicShapeInfo(
std::map<std::string, std::vector<int>> min_input_shape,
std::map<std::string, std::vector<int>> max_input_shape,
std::map<std::string, std::vector<int>> optim_input_shape,
bool disable_trt_plugin_fp16 = false);
//
// TensorRT 动态 shape 的自动推导,使用示例参考 https://github.com/PaddlePaddle/Paddle-Inference-Demo/blob/d6c1aac35fa8a02271c9433b0565ff0054a5a82b/c++/paddle-trt/tuned_dynamic_shape
// 参数: shape_range_info_path - 统计生成的 shape 信息存储文件路径,当设置为空时,在运行时收集
// allow_build_at_runtime - 是否开启运行时重建 TensorRT 引擎功能,当设置为 true 时,输入 shape
// 超过 tune 范围时会触发 TensorRT 重建。当设置为 false 时,输入 shape
// 超过 tune 范围时会引起推理出错
// 返回:None
void EnableTunedTensorRtDynamicShape(const std::string& shape_range_info_path = "",
bool allow_build_at_runtime = true);
// 启用 TensorRT OSS 进行 ERNIE / BERT 预测加速(示例代码 https://github.com/PaddlePaddle/Paddle-Inference-Demo/tree/master/c%2B%2B/ernie-varlen )
// 参数:None
// 返回:None
void EnableTensorRtOSS();
// 判断是否启用 TensorRT OSS
// 参数:None
// 返回:bool - 是否启用 TensorRT OSS
bool tensorrt_oss_enabled();
/// 启用 TensorRT DLA 进行预测加速
/// 参数:dla_core - DLA 设备的 id,可选 0,1,...,DLA 设备总数 - 1
/// 返回:None
void EnableTensorRtDLA(int dla_core = 0);
/// 判断是否已经开启 TensorRT DLA 加速
/// 参数:None
/// 返回:bool - 是否已开启 TensorRT DLA 加速
bool tensorrt_dla_enabled();
代码示例 (1):使用 TensorRT FP32 / FP16 / INT8 进行预测
// 创建 Config 对象
paddle_infer::Config config("./model/mobilenet.pdmodel", "./model/mobilenet.pdiparams");
// 启用 GPU 进行预测
config.EnableUseGpu(100, 0);
// 启用 TensorRT 进行预测加速 - FP32
config.EnableTensorRtEngine(1 << 28, 1, 3,
paddle_infer::PrecisionType::kFloat32, false, false);
// 通过 API 获取 TensorRT 启用结果 - true
std::cout << "Enable TensorRT is: " << config.tensorrt_engine_enabled() << std::endl;
// 开启 TensorRT 显存优化
config.EnableTensorRTMemoryOptim();
// 启用 TensorRT 进行预测加速 - FP16
config.EnableTensorRtEngine(1 << 28, 1, 3,
paddle_infer::PrecisionType::kHalf, false, false);
// 通过 API 获取 TensorRT 启用结果 - true
std::cout << "Enable TensorRT is: " << config.tensorrt_engine_enabled() << std::endl;
// 启用 TensorRT 进行预测加速 - Int8
config.EnableTensorRtEngine(1 << 28, 1, 3,
paddle_infer::PrecisionType::kInt8, false, true);
// 通过 API 获取 TensorRT 启用结果 - true
std::cout << "Enable TensorRT is: " << config.tensorrt_engine_enabled() << std::endl;
代码示例 (2):使用 TensorRT 动态 Shape 进行预测
// 创建 Config 对象
paddle_infer::Config config("./model/mobilenet.pdmodel", "./model/mobilenet.pdiparams");
// 启用 GPU 进行预测
config.EnableUseGpu(100, 0);
// 启用 TensorRT 进行预测加速 - Int8
config.EnableTensorRtEngine(1 << 29, 1, 1,
paddle_infer::PrecisionType::kInt8, false, true);
// 开启 TensorRT 显存优化
config.EnableTensorRTMemoryOptim();
// 设置模型输入的动态 Shape 范围
std::map<std::string, std::vector<int>> min_input_shape = {{"image", {1, 1, 3, 3}}};
std::map<std::string, std::vector<int>> max_input_shape = {{"image", {1, 1, 10, 10}}};
std::map<std::string, std::vector<int>> opt_input_shape = {{"image", {1, 1, 3, 3}}};
// 设置 TensorRT 的动态 Shape
config.SetTRTDynamicShapeInfo(min_input_shape, max_input_shape, opt_input_shape);
代码示例 (3):使用 TensorRT OSS 进行预测(完整示例)
// 创建 Config 对象
paddle_infer::Config config("./model/ernie.pdmodel", "./model/ernie.pdiparams");
// 启用 GPU 进行预测
config.EnableUseGpu(100, 0);
// 启用 TensorRT 进行预测加速
config.EnableTensorRtEngine();
// 启用 TensorRT OSS 进行预测加速
config.EnableTensorRtOSS();
// 通过 API 获取 TensorRT OSS 启用结果 - true
std::cout << "Enable TensorRT is: " << config.tensorrt_oss_enabled() << std::endl;