第 5 章:使用 Vulkan 进行推理
Are you an LLM? You can read better optimized documentation at /public/blog\2025-02\ncnn\05.vulkan.md for this page in Markdown format第 5 章:使用 Vulkan 进行推理 1. 什么是 Vulkan2. NCNN 如何使用 Vulkan2.1 安装 Vulkan 支持2.2 在项目中使用 Vulkan2.3 使用合适的分配器2.4 使用零复制的一致的内存设备2.5 使用 CPU/GPU 混合推理3. 零复制的 GPU 链式推理4. 批量推理5. 控制优化策略6. 调试模型参考1. 什么是 Vulkan Vulkan 是一种新的图形和计算 API,它是一种低开销、跨平台、高性能的 3D 图形和计算 API。Vulkan 旨在提供更好的性能和更均衡的 CPU/GPU 使用,同时提供更好的多线程能力。
Vulkan 被认为更便捷的方案,并且得到多种供应商和跨平台低层图形 API 的支持。相反,CUDA 仅在 NVIDIA 设备上可用,Metal 仅在 MacOS 和 iOS 上可用,而 OpenCL 则在 Android 7.0+ 中禁被禁用,并且不支持 iOS。
2. NCNN 如何使用 Vulkan Vulkan 的支持性:
支持性WindowsLinuxAndroidMaciOSIntel✅✅❔❔❌AMD✅✅❌❔❌Nvidia✅✅❔❌❌Qcom❌❌✅❌❌Apple❌❌❌❔✅ARM❌❔❔❌❌2.1 安装 Vulkan 支持 在使用之前,我们需要开启 Vulkan 的支持。在你的系统上安装 Vulkan 支持,一些系统已经内置支持了 Vulkan:
bashsudo dnf install vulkan-devel2.2 在项目中使用 Vulkan 在编译项目时指定参数来开启 Vulkan 功能:
bashcmake -DNCNN_VULKAN=ON ..为了支持 Vulkan 推理,还需要在 Net 对象中启用 Vulkan:
cppncnn::Net net;
net.opt.use_vulkan_compute = 1;指定使用 Vulkan 的网络运行在哪个 GPU 上:
cpp// 获取 GPU 数量
int gpu_count = ncnn::get_gpu_count();
// 设置 Vulkan 设备
net.set_vulkan_device(0); // 将使用 device-02.3 使用合适的分配器 cpp// 获取 Vulkan 的分配器
ncnn::VkAllocator* blob_vkallocator = vkdev.acquire_blob_allocator();
ncnn::VkAllocator* staging_vkallocator = vkdev.acquire_blob_allocator();
net.opt.blob_vkallocator = blob_vkallocator;
net.opt.workspace_vkallocator = blob_vkallocator;
net.opt.staging_vkallocator = staging_vkallocator;
// 推理
// ...
// 推理后
vkdev.reclaim_blob_allocator(blob_vkallocator);
vkdev.reclaim_staging_allocator(staging_vkallocator);2.4 使用零复制的一致的内存设备 cppncnn::VkMat blob_gpu;
ncnn::Mat mapped = blob_gpu.mapped();
// 下面可以直接使用 mapped.data2.5 使用 CPU/GPU 混合推理 cppncnn::Extractor ex_cpu = net.create_extractor();
ncnn::Extractor ex_gpu = net.create_extractor();
ex_cpu.set_vulkan_compute(false);
ex_gpu.set_vulkan_compute(true);
#pragma omp parallel sections
{
#pragma omp section
{
ex_cpu.input();
ex_cpu.extract();
}
#pragma omp section
{
ex_gpu.input();
ex_gpu.extract();
}
}3. 零复制的 GPU 链式推理 cppncnn::Extractor ex1 = net1.create_extractor();
ncnn::Extractor ex2 = net2.create_extractor();
ncnn::VkCompute cmd(&vkdev);
ncnn::VkMat conv1;
ncnn::VkMat conv2;
ncnn::VkMat conv3;
ex1.input("conv1", conv1);
ex1.extract("conv2", conv2, cmd);
ex2.input("conv2", conv2);
ex2.extract("conv3", conv3, cmd);
cmd.submit();
cmd.wait();4. 批量推理 cppint max_batch_size = vkdev->info.compute_queue_count;
ncnn::Mat inputs[1000];
ncnn::Mat outputs[1000];
#pragma omp parallel for num_threads(max_batch_size)
for (int i = 0; i < 1000; i++) {
ncnn::Extractor ex = net1.create_extractor();
ex.input("data", inputs[i]);
ex.extract("prob", outputs[i]);
}5. 控制优化策略 禁用低精度优化,使用 float32 精度:
cppncnn::Net net;
net.opt.use_fp16_packed = false;
net.opt.use_fp16_storage = false;
net.opt.use_fp16_arithmetic = false;
net.opt.use_int8_storage = false;
net.opt.use_int8_arithmetic = false;6. 调试模型 目前 Vulkan 不能直接进行调试,需要修改 NCNN 的源码 src/gpu.cpp 中的宏指令:
cpp#define ENABLE_VALIDATION_LAYER 1修改为 1 可以获得调试输出。
参考 [1] https://github.com/Tencent/ncnn/wiki/vulkan-notes