背景
在 Android 端侧 AI 推理场景中,模型输入往往来自于相机预览流或图像处理器。传统的做法是将数据从 Vendor 进程拷贝到 App 进程,再拷贝给推理引擎。对于 4K 图像或高频推理任务,这种 memcpy 会显著增加延迟并消耗大量 CPU。ION(或现代 Android 中的 DMA-BUF)是 Android 系统提供的内存管理器,允许不同硬件组件(GPU、NPU、DSP)以及不同进程之间共享同一块物理内存,实现真正的『零拷贝』。
核心原理
- 分配 (Allocation):在服务端进程从 ION 堆中申请内存。
- 导出 (Export):将申请到的内存句柄转化为文件描述符 (File Descriptor, FD)。
- 传递 (Sharing):利用 Android Binder 机制将 FD 跨进程传递给消费端。
- 映射 (Mapping):消费端通过 mmap 将 FD 映射到本地虚拟内存,并封装为推理引擎的张量对象。
实战:分配并导出 ION 内存
首先,在生产者进程(通常是 Native 层)中使用 C 语言接口操作 /dev/ion。
#include <linux/ion.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
int ion_alloc_shared_fd(size_t size) {
// 1. 打开 ION 设备
int ion_fd = open(\"/dev/ion\", O_RDONLY);
if (ion_fd < 0) return -1;
// 2. 申请内存 (针对 Android 10 以下,新版本建议使用 dma-buf heap)
struct ion_allocation_data alloc_data = {
.len = size,
.align = 4096,
.heap_id_mask = 1 << 0, // ION_HEAP_SYSTEM_MASK
.flags = 0
};
if (ioctl(ion_fd, ION_IOC_ALLOC, &alloc_data) < 0) return -1;
// 3. 将 Handle 转换为可传递的 FD
struct ion_fd_data fd_data = { .handle = alloc_data.handle };
if (ioctl(ion_fd, ION_IOC_SHARE, &fd_data) < 0) return -1;
return fd_data.fd; // 这个 FD 可以通过 Binder 传递
}
实战:在消费端封装为 PyTorch 张量
一旦消费端收到 FD,我们可以使用 torch::from_blob 直接在原始物理内存上构建张量,无需任何拷贝。
#include <torch/extension.h>
#include <sys/mman.h>
// 假设 fd 已经通过 Binder 获取
at::Tensor map_ion_to_tensor(int fd, size_t size, std::vector<int64_t> shape) {
// 1. 映射内存
void* ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) return {};
// 2. 封装为 PyTorch 张量
auto options = torch::TensorOptions().dtype(torch::kFloat32).device(torch::kCPU);
// 注意:如果是 GPU 推理,通常需要将 FD 绑定到 OpenCL 内存或 Vulkan 图像
return torch::from_blob(ptr, shape, [ptr, size](void*) {
munmap(ptr, size); // 注册析构回调以释放映射
}, options);
}
进阶技巧:缓存一致性 (Cache Coherency)
当 CPU 写入内存而 GPU 随后读取时,必须确保缓存已刷入物理内存。ION 提供了专门的 IOCTL 来处理同步:
struct ion_custom_data custom_data;
struct ion_cache_ops cache_ops = {
.handle = handle,
.virt = ptr,
.offset = 0,
.len = size
};
custom_data.cmd = ION_IOC_CLEAN_CACHES; // 或 ION_IOC_INV_CACHES
custom_data.arg = (unsigned long)&cache_ops;
ioctl(ion_fd, ION_IOC_CUSTOM, &custom_data);
总结
通过 ION 实现的零拷贝方案在 Android 高性能推理中至关重要。它不仅降低了 20%~50% 的数据传递耗时,还能显著降低系统的整体内存占用。在 Android 11+ 之后,建议优先查看 /dev/dma_heap/ 接口,其封装更现代但逻辑完全一致。
汤不热吧