ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Windows下Paddle Inference预编译包的使用:CUDA与TensorRT加速部署指南

Windows下Paddle Inference预编译包的使用:CUDA与TensorRT加速部署指南 简介面向Windows平台开发者与深度学习推理工程师这套Paddle Inference 3.0.0预编译开发包基于CUDA 11.8、cuDNN 8.6.0与TensorRT 8.5.1.7构建配合MKL和AVX优化选用VS2019工具链可直接用于x86-64环境下的PaddlePaddle模型部署。包体共623个文件主要包括569个头文件与15个hpp头文件用于二次开发接口13个lib与2个exp库文件用于链接5个dll与2个manifest用于运行时加载另有12个proto和1个pb定义序列化协议整体压缩后约528MB。目前已有136人学习下载。对需要快速接入推理引擎、省去自行编译依赖的开发者这套资源提供了完整头文件、导入库和运行时动态库还包含MKL、MKLDNN等常用依赖组件配置后即可开始模型加载、前向推理与性能调试能有效缩短环境搭建周期也方便与既有VS2019工程集成。无论是快速验证推理效果还是集成进已有C服务都能减少手动配置CUDA运行时和TensorRT组件的繁琐步骤直接进入业务开发环节。1. 这个 paddle-inference 开发包x86-64 CUDA 11.8 TRT 8.5.1.7 的预编译交付省掉三小时源码构建x86-64-cuda11.8-cudnn8.6.0-trt8.5.1.7-mkl-avx-vs2019-paddle-inference-3.0.0.zip这种包名第一眼望上去像一串版本号在打架用起来才知道它解决的是最烦人的问题你在 Windows x86-64 机器上部署 Paddle Inference本机装了 VS2019又想用 GPU 和 TensorRT 加速却不想从源码重新编译 Paddle。这个包把 CUDA 11.8、cuDNN 8.6.0、TensorRT 8.5.1.7、MKL 与 AVX 优化全部预编译好解压后接入自己的 C/Python 工程就能调用。适合三类人不想再被源码编译吊打的业务开发者、把 Paddle 模型接到生产推理服务的工程师、以及用 GTX 1070 到 4060 Ti 这类中端显卡做本地验证的算法同学。2. 解包与版本匹配先看文件结构再对齐你机器的 CUDA 环境2.1 拆包后你会看到什么paddle 与 third_party 的分工解压之后通常是一个paddle_inference_install_dir目录里面不是安装器而是纯头文件、导入库、DLL 和第三方依赖的集合。我第一次拆这种包时也犹豫过要不要跑一个 setup.exe实际上没有直接把整个目录挪到D:\packages\下就能当本地库引用。路径里面是什么工程里怎么用paddle/includepaddle_inference_api.h等头文件CMake 里include_directoriespaddle/libpaddle_inference.lib导入库链接时指定paddle_inference.libpaddle/binpaddle_inference.dll及 CUDA/cuDNN 相关 DLL运行时搜索路径或复制到 exe 旁third_party/installpaddle2onnx、mklml、mkldnn等按需加入 PATH缺少会启动报错这里有个容易忽略的点paddle/lib里的.lib是导入库真正执行时用的是paddle/bin里的.dll。所以很多人在链接阶段没问题一运行就弹“找不到paddle_inference.dll”就是因为只配了 include/lib没把 bin 目录交给系统。Windows 下 DLL 搜索顺序是 exe 所在目录、系统目录、PATH这个顺序决定了很多莫名的启动失败。2.2 版本编号为什么这样锁CUDA 11.8 与 VS2019 的关系这套包的 GPU 侧依赖链是 CUDA 11.8 cuDNN 8.6.0 TensorRT 8.5.1.7这个组合不是随便拼的。Paddle Inference 3.0.0 在 Windows x86-64 上的官方预编译发布包主要就是用 VS2019 工具集和 CUDA 11.8 构建的。VS2019 对应的 MSVC 14.2 ABI 是 Paddle 官方 Windows 构建的标准如果你本机是 VS2022编译自己代码时可以通过-T v142指定工具集来兼容不会太麻烦。为什么不是 CUDA 12.x 或 13.x因为预编译包里的算子 kernel 和运行库绑定的是 11.8 的 ABI。驱动侧新版本能向后兼容老 CUDA也就是你用 4060 Ti 配新驱动跑 CUDA 11.8 的程序没有任何问题但反过来你用一个 CUDA 12 编译出来的第三方库去链接 Paddle 3.0.0 的预编译包就可能出现算子 kernel 找不到、推理中途退到 CPU、甚至直接cudaErrorNoKernelImageForDevice。网上搜“cuda安装失败”“cuda多版本安装”翻车的人八成不是驱动装不上而是把 CUDA Toolkit 12 和需要用 CUDA 11.8 ABI 的库混在了一起。还有个小坑很多人判断自己机器有没有装 CUDA习惯去找 CUDA Samples。实际上 Samples 只是示例工程不决定 runtime 能不能跑。我一般看nvidia-smi驱动版本和paddle/bin里的 DLL 是否齐全就够了。2.3 环境检查三条命令看清本机底牌拿到包以后不要急着配工程先在 Windows 终端里跑下面三条命令nvidia-smi nvcc --version where cl第一条看显卡驱动支持的 CUDA 版本第二条看你有没有装 CUDA Toolkit第三条确认 MSVC 编译器能不能被where找到。注意nvidia-smi顶部显示的 CUDA Version 是驱动支持的“最高上限”不是说你已经安装了对应版本的 toolkit很多人在这一步被误导过。如果你用这个包做纯推理实际上不需要完整安装 CUDA Toolkit只要驱动够新、cuDNN 和 TRT 的 DLL 找得到就行。常见做法是把检查结果记下来驱动版本大于 525 基本能覆盖 CUDA 11.8cl找不到就把 VS2019 的“使用 C 的桌面开发”工作负载补上。顺便说一句这个包是 Windows 原生版别拿 WSL 里的 CUDA 路径来套。WSL 配置得再好/usr/local/cuda和 Windows 的 DLL 搜索机制是两套逻辑网上关于“wsl安装cuda”“ubuntu安装cuda”的教程在普通 Ubuntu 里适用在这里不如把精力花在 Windows 的 PATH 上。2.4 环境变量配置让 DLL 能被找到我习惯先定义一个根目录变量再把它下面的关键路径挂到 PATHset PADDLE_INFERENCE_ROOTD:\packages\paddle_inference_install_dir set PATH%PADDLE_INFERENCE_ROOT%\paddle\bin;%PADDLE_INFERENCE_ROOT%\third_party\install\mklml\lib;%PATH%PADDLE_INFERENCE_ROOT是给 CMake 用的缓存变量PATH里加的两段分别解决paddle_inference.dll和mklml.dll的搜索。如果你的机器上已经装了其他 CUDA 版本PATH 里可能出现 CUDA 12.6、CUDA 11.8 混在一起的情况这时候建议不要依赖全局 PATH而是把paddle/bin下的 DLL 直接复制到你自己 exe 的旁边这是最稳的土办法。很多人问我“cuda多版本安装”怎么处理我的答案永远是开发编译用你完整装的 toolkit交付运行时用和预编译包配套的 DLL二者分开不要指望一个 PATH 通吃。3. CMake 链接 paddle_inference三步建立 VS2019 工程并跑通 demo3.1 CMake 工程骨架最小可用 CMakeLists这个包没有提供find_package(Paddle)的 CMake config 模块所以最直接的方式是手动指 include 和 lib 路径。我不会一上来就用复杂的FetchContent那样只会增加变量。cmake_minimum_required(VERSION 3.20) project(paddle_demo LANGUAGES CXX) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(PADDLE_ROOT D:/packages/paddle_inference_install_dir CACHE PATH Paddle root) include_directories(${PADDLE_ROOT}/paddle/include) link_directories(${PADDLE_ROOT}/paddle/lib) add_executable(demo main.cpp) target_link_libraries(demo paddle_inference) if(MSVC) target_compile_options(demo PRIVATE /MD) endif()逻辑很简单include_directories指向头文件link_directories指向导入库所在目录然后 target 链接paddle_inference.lib。这里有个参数要特别说清楚/MD是让工程使用动态运行库。Paddle 预编译包是按/MD构建的你自己的代码如果默认用了/MT链接阶段就会出现运行库冲突报错里通常带着LNK2038或_ITERATOR_DEBUG_LEVEL。所以我在 CMake 里强制加了/MD这是预先止血。3.2 一个能跑的推理 demo加载模型、喂数据、取输出写一个最简单的main.cpp只做一件事加载模型跑一次前向把输出 shape 打出来。这里以图片分类模型的固定输入为例输入是[1, 3, 224, 224]的浮点数据。#include paddle_inference_api.h #include iostream #include vector int main() { paddle_infer::Config config; // 模型与参数文件分开set_model 第一个参数是结构文件第二个是权重文件 config.SetModel(model/model.pdmodel, model/model.pdparams); // 指定显存 1024MB用 0 号 GPU config.EnableUseGpu(1024, 0); auto predictor paddle_infer::CreatePredictor(config); // 获取输入输出的名称方便按名字取句柄 auto input_names predictor-GetInputNames(); auto output_names predictor-GetOutputNames(); auto input_handle predictor-GetInputHandle(input_names[0]); std::vectorint64_t shape{1, 3, 224, 224}; input_handle-Reshape(shape); // 这里只是示例用 1.0f 填充整个张量 std::vectorfloat input_data(1 * 3 * 224 * 224, 1.0f); input_handle-CopyFromCpu(input_data.data()); predictor-Run(); auto output_handle predictor-GetOutputHandle(output_names[0]); auto output_shape output_handle-shape(); std::cout output dims:; for (auto d : output_shape) { std::cout d; } std::cout std::endl; return 0; }SetModel和EnableUseGpu是整个推理配置的核心前者决定从哪个目录读模型后者决定是否走 GPU。如果你忘了调EnableUseGpuPaddle 默认会走 CPU 推理模型能跑但速度不对这种“假成功”比报错更隐蔽。CopyFromCpu要求数据内存连续vector 的data()拿到的就是连续内存所以直接传指针没问题。这里我故意把输入全部填成 1.0f只是为了验证链路通不通真实场景要换你模型对应的预处理逻辑。运行一次后如果终端打印出了 output 的 shape说明链接和运行时环境已经通了。在 Windows 终端里跑 exe 时如果报找不到paddle_inference.dll回到上一章把 PATH 配置好再试。3.3 VS2019 构建CMake 命令行与 Release x64 的选择项目根目录下新建build目录然后执行cmake -G Visual Studio 16 2019 -A x64 -DCMAKE_BUILD_TYPERelease .. cmake --build . --config Release注意-A x64必须带这个包是 x86-64 架构默认的 Win32 配置会直接让你在链接阶段怀疑人生。CMake 生成完 VS 工程后也可以用 Visual Studio 直接打开.sln文件但要把解决方案配置切成Release平台切成x64。我见过不少人卡在这里编译过了一运行就崩原因是 Debug 模式下_ITERATOR_DEBUG_LEVEL和 Paddle 预编译库不一致这是老生常谈的坑。在这个包上Debug 工作区基本没有意义直接放弃 Debug 构建能省很多时间。如果你确实需要在 Debug 下调试业务代码那就要保证你的代码也用/MD并在预处理定义里把_ITERATOR_DEBUG_LEVEL统一为 0这个方案可行但每次升级 Paddle 版本都容易复发。我的习惯是业务逻辑单独拆成动态库调demo 主程序永远跑 Release调试交给日志而不是断点。4. 上 TensorRT 加速FP16、动态 shape 与 engine 缓存三件套4.1 TRT 8.5.1.7 与 Paddle 3.0.0 的关系Paddle Inference 里的 TensorRT 不是独立部署的推理服务而是作为一个高性能后端被 Paddle 调度。Paddle 会把模型切成若干个算子子图能交给 TensorRT 的算子会组成一个 subgraph其余算子留在 Paddle 原生执行器上。这个“分组”的粒度由min_subgraph_size控制。TRT 8.5.1.7 绑在 Paddle 3.0.0 上算子 bridge 已经覆盖了常见 CV、NLP 模型但如果你自己去装一个 TRT 9.x覆盖面未必更广反而可能因为 op parser 版本不一致导致子图构建失败。所以我建议除非明确遇到算子不支持否则就用包内配套的版本。4.2 C 工程里的 TRT 开关精度与工作区在 C 里打开 TRT 就是在Config上加一行EnableTensorRtEngine。下面是我常用的配置config.EnableTensorRtEngine( 1 28, // 工作区大小256MB单位字节 4, // max_batch_size 3, // min_subgraph_size子图最小算子数 paddle_infer::PrecisionType::kHalf, // 精度Half 即 FP16 true, // use_static允许引擎序列化 false // use_calib_mode不做校准 );工作区1 28是给 TRT 做 kernel 选择的临时显存不是整个模型的最大显存占用。min_subgraph_size太小会导致大量小算子频繁切换进 TRT调度开销反而变大太大又会让很多本可以加速的算子留在 Paddle 里。我一般从 3 开始试如果日志里显示 subgraph 数量少就调大如果显示算子不被支持就调小或者禁用个别算子。PrecisionType::kHalf对大多数模型都有明显加速但如果模型里有对精度敏感的层比如某些分割网络可以退回到kFloat32先保证正确性再谈速度。4.3 Python 侧与 engine 缓存怎么让第二次启动变快实际部署时很多人用 Python 写推理服务接口和 C 基本对应。Paddle Inference 的 Python 端接口是paddle.inference开启 TensorRT 的写法如下import paddle.inference as paddle_infer config paddle_infer.Config(model/model.pdmodel, model/model.pdparams) config.enable_use_gpu(1024, 0) config.enable_tensorrt_engine( workspace_size1 28, max_batch_size4, min_subgraph_size3, precision_modepaddle_infer.PrecisionType.Half, use_staticTrue, use_calib_modeFalse ) # 持久化 TRT engine第二次启动直接读缓存 config.set_trt_engine_cache_dir(trt_cache) predictor paddle_infer.create_predictor(config)use_staticTrue和set_trt_engine_cache_dir(trt_cache)是配套的。第一次运行TRT 会把生成的 engine 写到trt_cache目录第二次启动时如果模型结构没变、GPU 型号没变、TRT 版本没变就直接加载缓存文件跳过子图构建和 kernel 选择启动时间能从几十秒压到两三秒。这个技巧在服务端重启场景下非常有用。需要提醒的是engine 缓存和 GPU 架构强绑定你在一张 GTX 1070 上生成的缓存拿到 RTX 4060 Ti 上会失效这是 TensorRT 的机制不是 bug。4.4 算子掉 GPU 的判断与处理打开 TRT 后最常遇到的问题是模型跑起来却没看到预期提速。我会先看日志里有没有类似trt subgraph的信息以及 GPU 利用率有没有明显变化。如果大部分算子没进 TRT常见原因是模型里存在 TRT 不支持的算子比如某些自定义 Op、动态 shape 提取类算子。Paddle 提供禁用指定算子进 TRT 的接口config.disable_trt_ops([some_op])禁用后这个算子会留在 Paddle 上执行其余算子仍然走 TRT子图仍然成立。这里有个反直觉的点禁用一个算子不一定变慢因为 TRT 子图构建失败时整个图都会退回 Paddle反而更慢。所以我的习惯是先全量开启跑一遍看日志和耗时如果明显有算子卡住再逐个禁用观察 subgraph 数量和总延迟变化。那种“开了 TRT 就一定快”的预期要修正子图划分不合理时100 个算子拆成 3 个子图调度开销可能抵消部分优化。5. 避坑五个 Windows 侧兼容性问题与现场处理5.1 LNK2038 运行库冲突Debug 和 Release 的 ABI 对抗现象用 VS2019 编译 C demo链接阶段报LNK2038 mismatch detected for _ITERATOR_DEBUG_LEVEL有时还伴随_ITERATOR_DEBUG_LEVEL值不一致的详细提示。原因Paddle 预编译库是 Release /MD构建的内部_ITERATOR_DEBUG_LEVEL0。你的工程如果用了 Debug 配置标准库迭代器调试级别变成 2两者 ABI 就不兼容MSVC 链接器直接拒绝。解决把整个工程切到 Release x64并在 CMake 里统一加/MD。如果必须用 Debug 调试业务逻辑那就把_ITERATOR_DEBUG_LEVEL通过预处理定义显式改成 0运行时库选/MD但每次换环境都要检查一遍别依赖记忆。5.2 找不到 cuDNN 或 mkl DLL复制到 exe 旁边现象程序运行时弹窗“无法找到cudnn64_8.dll”或“无法找到mklml.dll”代码明明在编译时没有任何报错。原因链接阶段只用了.lib运行阶段需要对应的.dll。你的 PATH 里可能已经有 CUDA Toolkit但 cuDNN 8.6.0 的 DLL 在paddle/bin里mklml 在third_party/install/mklml/lib里这两个目录没被系统搜索到。解决把paddle/bin下所有 DLL以及third_party/install/mklml/lib、third_party/install/mkldnn/lib下的 DLL全部复制到生成 exe 的同级目录。这是最土也最可靠的办法发布时还能形成一个独立的绿色目录不用依赖目标机器 PATH 配置。注意 cudnn64_8.dll 是 cuDNN 8.x 的命名8.6.0 就是这个名字别去找 cudnn64_9.dll。5.3 看起来在用 GPU实际跑的是 CPU忘记开 EnableUseGpu现象模型推理结果正确但nvidia-smi里看不到 python 或 demo 进程占用显存日志里也没有出现GPU相关打印。原因paddle_inference默认优先加载 CPU 算子。你没有调用config.enable_use_gpu或EnableUseGpuPaddle 就安静地用 CPU 执行不报任何错误。尤其是那些由旧脚本改来的代码很容易漏掉这一行。解决代码里显式开启 GPU。C 里是config.EnableUseGpu(1024, 0)Python 里是config.enable_use_gpu(1024, 0)。显存参数给 256MB 以上0 是 GPU 卡号。启动后立刻用nvidia-smi观察显存占用确认进程里有python.exe或demo.exe占用了显存。5.4 显存够却 OOM工作区设太大、没开显存优化现象4060 Ti 8GB 显存模型本身不大却报cudaMalloc failed: out of memory网上查会看到类似“cuda malloc disabled”的说法实际上是分配失败。原因enable_tensorrt_engine的workspace_size可能拍脑袋设成了1 30即 1GB再加上模型运行时的激活显存、Paddle 的显存池分流了本就不宽裕的 8GB。另外 Windows 图形界面本身也会占用一些显存不能按型号标称值满打满算。解决把 workspace 降到1 28或更小同时开启config.enable_memory_optim()让 Paddle 复用显存块。启动时nvidia-smi看实际占用别让进程占用超过总显存的 70%。这条在 GTX 1070 上尤其明显8GB 显存去掉桌面占用后能分给推理的可能只有 6GB 上下。5.5 TRT engine 跨机失效换显卡型号就翻车现象在开发机上用 TRT 缓存正常启动把整个目录拷到另一台相同系统版本、但显卡型号不同的机器上运行时程序报 TensorRT engine 版本不匹配或者启动直接失败。原因TensorRT 序列化后的 engine 文件绑定 GPU 架构、TRT 版本和 shape 范围。开发机如果是 RTX 3060目标是 GTX 1070两者的 SM 架构不同engine 缓存无法复用。解决把trt_cache目录和shape_range.pbtxt如果打开了动态 shape从项目交付物里排除让生产机第一次启动时重新生成 engine或者干脆保证开发机和生产机显卡型号完全相同。如果你用的是多卡机器还要注意set_trt_engine_cache_dir生成的缓存文件名里可能包含设备信息复制缓存时要连目录一起复制。这条是我实际踩过的后来我把“engine 缓存不进版本库”写进了部署清单每次发版本都自动检查一次。6. 验证 GPU 真正生效日志、显存与工程习惯6.1 一条 Python 验证脚本三行代码确认推理后端C 工程验证步骤多我一般先写一条 Python 脚本做环境冒烟测试确认开发包本身没问题再回过去查 C 环节import paddle.inference as paddle_infer config paddle_infer.Config(model/model.pdmodel, model/model.pdparams) config.enable_use_gpu(256, 0) config.enable_tensorrt_engine( workspace_size1 28, max_batch_size1, min_subgraph_size3, precision_modepaddle_infer.PrecisionType.Half, use_staticTrue, use_calib_modeFalse ) predictor paddle_infer.create_predictor(config) print(predictor.get_input_names()) print(predictor.get_output_names())如果输出能打印出模型的输入输出张量名说明模型解析成功。此时再开一个终端跑nvidia-smi如果看到当前进程占用了显存并且python.exe出现在 GPU 进程列表里就说明 CUDA cuDNN TRT 的链路已经完整打通。如果显存没变化优先检查enable_use_gpu是否真的执行到了其次检查是不是用了 CPU 版的paddlepaddlePython 包这个和开发包不是一回事。6.2 一个落地习惯本机生成 TRT 缓存后再复制用这个包做工程交付时我习惯把“TRT 缓存预热”做成发布流程中的一个显式步骤在开发机上先跑一次完整推理确认trt_cache目录生成了若干.engine文件然后把模型、缓存、DLL 一起打包。生产机如果和开发机同型号 GPU直接拷贝目录即可如果型号不同就把trt_cache删掉让生产机首次启动时自己生成。有一回我图省事把开发机生成的 engine 直接拷到了另一台不同型号的卡上启动就报 TRT 版本不匹配整个服务起不来。从那以后我每次接触这种带 TRT 的预编译开发包都会强制走一遍“先冒烟、再开 TRT、查显存、验缓存”的流程模型结构变一次就重新预热一次。这套包分担了最耗时的源码编译工作剩下的部署细节就是耐心校验这些运行条件。希望帮到你。本文还有配套的精品资源点击获取
返回列表