• C++ 预测 API介绍
    • 内容
    • NativePredictor使用
      • NativePredictor 使用样例
  • AnalysisPredictor使用
    • AnalysisPredictor 使用样例
  • 输入输出的管理
    • PaddleTensor 的使用
    • ZeroCopyTensor的使用
  • 多线程预测
  • 性能建议

    C++ 预测 API介绍

    为了更简单方便的预测部署,PaddlePaddle 提供了一套高层 API 预测接口。

    预测库包含:

    • 头文件主要包括:
      • paddle_analysis_config.h
      • paddle_api.h
      • paddle_inference_api.h
    • 库文件:
      • libpaddle_fluid.so
      • libpaddle_fluid.a下面是详细介绍。

    内容

    • NativePredictor使用
    • AnalysisPredictor使用
    • 输入输出的管理
    • 多线程预测
    • 性能建议

    NativePredictor使用

    NativePredictor为原生预测引擎,底层由 PaddlePaddle 原生的 forward operator 组成,可以天然支持所有Paddle 训练出的模型

    NativePredictor 使用样例

    1. #include "paddle_inference_api.h"
    2.  
    3. namespace paddle {
    4. // 配置NativeConfig
    5. void CreateConfig(NativeConfig *config, const std::string& model_dirname) {
    6. config->use_gpu=true;
    7. config->device=0;
    8. config->fraction_of_gpu_memory=0.1;
    9.  
    10. /* for cpu
    11. config->use_gpu=false;
    12. config->SetCpuMathLibraryNumThreads(1);
    13. */
    14.  
    15. // 设置模型的参数路径
    16. config->prog_file = model_dirname + "model";
    17. config->param_file = model_dirname + "params";
    18. // 当模型输入是多个的时候,这个配置是必要的。
    19. config->specify_input_name = true;
    20. }
    21.  
    22. void RunNative(int batch_size, const std::string& model_dirname) {
    23. // 1. 创建NativeConfig
    24. NativeConfig config;
    25. CreateConfig(&config, model_dirname);
    26.  
    27. // 2. 根据config 创建predictor
    28. auto predictor = CreatePaddlePredictor(config);
    29.  
    30. int channels = 3;
    31. int height = 224;
    32. int width = 224;
    33. float *data = new float[batch_size * channels * height * width];
    34.  
    35. // 3. 创建输入 tensor
    36. PaddleTensor tensor;
    37. tensor.name = "image";
    38. tensor.shape = std::vector<int>({batch_size, channels, height, width});
    39. tensor.data = PaddleBuf(static_cast<void *>(data),
    40. sizeof(float) * (batch_size * channels * height * width));
    41. tensor.dtype = PaddleDType::FLOAT32;
    42. std::vector<PaddleTensor> paddle_tensor_feeds(1, tensor);
    43.  
    44. // 4. 创建输出 tensor
    45. std::vector<PaddleTensor> outputs;
    46. // 5. 预测
    47. predictor->Run(paddle_tensor_feeds, &outputs, batch_size);
    48.  
    49. const size_t num_elements = outputs.front().data.length() / sizeof(float);
    50. auto *data_out = static_cast<float *>(outputs.front().data.data());
    51. }
    52. } // namespace paddle
    53.  
    54. int main() {
    55. // 模型下载地址 http://paddle-inference-dist.cdn.bcebos.com/tensorrt_test/mobilenet.tar.gz
    56. paddle::RunNative(1, "./mobilenet");
    57. return 0;
    58. }

    AnalysisPredictor使用

    AnalysisConfig 创建了一个高性能预测引擎。该引擎通过对计算图的分析,完成对计算图的一系列的优化(Op 的融合, MKLDNN,TRT等底层加速库的支持 etc),大大提升预测引擎的性能。

    AnalysisPredictor 使用样例

    1. #include "paddle_inference_api.h"
    2.  
    3. namespace paddle {
    4. void CreateConfig(AnalysisConfig* config, const std::string& model_dirname) {
    5. // 模型从磁盘进行加载
    6. config->SetModel(model_dirname + "/model",
    7. model_dirname + "/params");
    8. // config->SetModel(model_dirname);
    9. // 如果模型从内存中加载,可以使用SetModelBuffer接口
    10. // config->SetModelBuffer(prog_buffer, prog_size, params_buffer, params_size);
    11. config->EnableUseGpu(10 /*the initial size of the GPU memory pool in MB*/, 0 /*gpu_id*/);
    12.  
    13. /* for cpu
    14. config->DisableGpu();
    15. config->EnableMKLDNN(); // 可选
    16. config->SetCpuMathLibraryNumThreads(10);
    17. */
    18.  
    19. // 当使用ZeroCopyTensor的时候,此处一定要设置为false。
    20. config->SwitchUseFeedFetchOps(false);
    21. // 当多输入的时候,此处一定要设置为true
    22. config->SwitchSpecifyInputNames(true);
    23. config->SwitchIrDebug(true); // 开关打开,会在每个图优化过程后生成dot文件,方便可视化。
    24. // config->SwitchIrOptim(false); // 默认为true。如果设置为false,关闭所有优化,执行过程同 NativePredictor
    25. // config->EnableMemoryOptim(); // 开启内存/显存复用
    26. }
    27.  
    28. void RunAnalysis(int batch_size, std::string model_dirname) {
    29. // 1. 创建AnalysisConfig
    30. AnalysisConfig config;
    31. CreateConfig(&config, model_dirname);
    32.  
    33. // 2. 根据config 创建predictor
    34. auto predictor = CreatePaddlePredictor(config);
    35. int channels = 3;
    36. int height = 224;
    37. int width = 224;
    38. float input[batch_size * channels * height * width] = {0};
    39.  
    40. // 3. 创建输入
    41. // 同NativePredictor样例一样,此处可以使用PaddleTensor来创建输入
    42. // 以下的代码中使用了ZeroCopy的接口,同使用PaddleTensor不同的是:此接口可以避免预测中多余的cpu copy,提升预测性能。
    43. auto input_names = predictor->GetInputNames();
    44. auto input_t = predictor->GetInputTensor(input_names[0]);
    45. input_t->Reshape({batch_size, channels, height, width});
    46. input_t->copy_from_cpu(input);
    47.  
    48. // 4. 运行
    49. CHECK(predictor->ZeroCopyRun());
    50.  
    51. // 5. 获取输出
    52. std::vector<float> out_data;
    53. auto output_names = predictor->GetOutputNames();
    54. auto output_t = predictor->GetOutputTensor(output_names[0]);
    55. std::vector<int> output_shape = output_t->shape();
    56. int out_num = std::accumulate(output_shape.begin(), output_shape.end(), 1, std::multiplies<int>());
    57.  
    58. out_data.resize(out_num);
    59. output_t->copy_to_cpu(out_data.data());
    60. }
    61. } // namespace paddle
    62.  
    63. int main() {
    64. // 模型下载地址 http://paddle-inference-dist.cdn.bcebos.com/tensorrt_test/mobilenet.tar.gz
    65. paddle::RunAnalysis(1, "./mobilenet");
    66. return 0;
    67. }

    输入输出的管理

    PaddleTensor 的使用

    PaddleTensor可用于NativePredictor和AnalysisPredictor,在 NativePredictor样例中展示了PaddleTensor的使用方式。PaddleTensor 定义了预测最基本的输入输出的数据格式,常用字段如下:

    • name,类型:string,用于指定输入数据对应的模型中variable的名字
    • shape,类型:vector<int>, 表示一个Tensor的shape
    • data,类型:PaddleBuf, 数据以连续内存的方式存储在PaddleBuf中,PaddleBuf可以接收外面的数据或者独立malloc内存,详细可以参考头文件中相关定义。
    • dtype,类型:PaddleType, 有PaddleDtype::FLOAT32, PaddleDtype::INT64, PaddleDtype::INT32三种, 表示 Tensor 的数据类型。
    • lod,类型:vector<vector<size_t>>,在处理变长输入的时候,需要对 PaddleTensor设置LoD信息。可以参考LoD-Tensor使用说明

    ZeroCopyTensor的使用

    ZeroCopyTensor的使用可避免预测时候准备输入以及获取输出时多余的数据copy,提高预测性能。只可用于AnalysisPredictor

    Note:使用ZeroCopyTensor,务必在创建config时设置config->SwitchUseFeedFetchOps(false)

    1. // 通过创建的AnalysisPredictor获取输入和输出的tensor
    2. auto input_names = predictor->GetInputNames();
    3. auto input_t = predictor->GetInputTensor(input_names[0]);
    4. auto output_names = predictor->GetOutputNames();
    5. auto output_t = predictor->GetOutputTensor(output_names[0]);
    6.  
    7. // 对tensor进行reshape
    8. input_t->Reshape({batch_size, channels, height, width});
    9.  
    10. // 通过copy_from_cpu接口,将cpu数据输入;通过copy_to_cpu接口,将输出数据copy到cpu
    11. input_t->copy_from_cpu<float>(input_data /*数据指针*/);
    12. output_t->copy_to_cpu(out_data /*数据指针*/);
    13.  
    14. // 设置LOD
    15. std::vector<std::vector<size_t>> lod_data = {{0}, {0}};
    16. input_t->SetLoD(lod_data);
    17.  
    18. // 获取tensor数据指针
    19. float *input_d = input_t->mutable_data<float>(PaddlePlace::kGPU); // CPU下使用PaddlePlace::kCPU
    20. int output_size;
    21. float *output_d = output_t->data<float>(PaddlePlace::kGPU, &output_size);

    多线程预测

    多线程场景下,每个服务线程执行同一种模型,支持 CPU 和 GPU。

    下面演示最简单的实现,用户需要根据具体应用场景做相应的调整

    1. auto main_predictor = paddle::CreatePaddlePredictor(config);
    2.  
    3. const int num_threads = 10; // 假设有 10 个服务线程
    4. std::vector<std::thread> threads;
    5. std::vector<decl_type(main_predictor)> predictors;
    6.  
    7. // 线程外创建所有的predictor
    8. predictors.emplace_back(std::move(main_predictor));
    9. for (int i = 1; i < num_threads; i++) {
    10. predictors.emplace_back(main_predictor->Clone());
    11. }
    12.  
    13. // 创建线程并执行
    14. for (int i = 0; i < num_threads; i++) {
    15. threads.emplace_back([i, &]{
    16. auto& predictor = predictors[i];
    17. // 执行
    18. CHECK(predictor->Run(...));
    19. });
    20. }
    21.  
    22. // 线程join
    23. for (auto& t : threads) {
    24. if (t.joinable()) t.join();
    25. }
    26.  
    27. // 结束

    性能建议

    • 在CPU型号允许的情况下,尽量使用带AVX和MKL的版本
    • CPU或GPU预测,可以尝试把NativeConfig改成AnalysisConfig来进行优化
    • 尽量使用ZeroCopyTensor避免过多的内存copy
    • CPU下可以尝试使用Intel的MKLDNN加速
    • GPU 下可以尝试打开TensorRT子图加速引擎, 通过计算图分析,Paddle可以自动将计算图中部分子图切割,并调用NVidia的 TensorRT 来进行加速。详细内容可以参考 Paddle-TRT 子图引擎