neon 和 sve 可以加速内存拷贝吗(copy_to_user/copy_from_user)
NEON / SVE 能否加速内存拷贝(copy_to_user / copy_from_user)
一、简短结论
| 技术 | 能否加速 memcpy | 能否加速 copy_to_user/copy_from_user |
|---|---|---|
| NEON | 技术上可以,但内核未采用 | ❌ 不适用 |
| SVE | 技术上可以,但内核未采用 | ❌ 不适用 |
| MOPS (ARMv8.8+) | ✅ 已采用 | ✅ 已采用 |
核心原因:NEON/SVE 在内核 uaccess 路径存在不可忽略的上下文开销和机制限制,使得收益为负。ARMv8.8 的 MOPS 指令集才是官方推荐的硬件加速方案。
二、为什么 NEON 不能加速 copy_to_user/copy_from_user
2.1 内核当前实现现状
内核的 __arch_copy_to_user 和 __arch_copy_from_user(arch/arm64/lib/copy_to_user.S、copy_from_user.S)全部使用通用寄存器(GPR),主循环用 ldp/stp 对每次 16 字节×8 = 128 字节/轮,完全不涉及 NEON 或 SVE 寄存器。
实际代码路径:
copy_template.S
├── MOPS (ARMv8.8+) → CPYP/CPYM/CPYE 硬件指令
└── 通用路径 → ldp/stp 对 (每次128B)
2.2 障碍 1:kernel_neon_begin/end 的开销不可接受
| 操作 | 延迟 |
|---|---|
kernel_neon_begin() 保存 32×128-bit NEON regs + FPSR/FPCR |
~200-500 ns |
| 后续返回用户空间时状态恢复 | ~200-500 ns |
| 合计 NEON 上下文切换开销 | ~0.5-2 µs |
| SVE 变体(VL=2048 bit,保存 32×Z reg) | 10-50+ µs |
对比一个典型 copy_to_user 拷贝 4KB 数据的时间(L1 命中场景):
| 操作 | 时间 |
|---|---|
4KB GPR ldp/stp 拷贝(L1 命中) |
~1-2 µs |
| NEON 上下文切换开销 | ~0.5-2 µs |
| SVE 上下文切换开销 | ~10-50 µs |
对于小拷贝(<4KB),NEON 上下文切换开销就占拷贝本身的 50-100%,SVE 则直接比拷贝本身还慢一个数量级。
2.3 障碍 2:无 unprivileged load/store 变体
copy_to_user/copy_from_user 必须使用 unprivileged load/store(LDTR/STTR),才能让内核在访问用户空间地址时触发正确的缺页异常机制。NEON 指令(如 LD1/ST1、LD2/ST2 等)没有对应的 unprivileged 变体,无法区分用户空间 vs 内核空间的地址访问。
无 unprivileged 变体意味着:
- 不能在内核态用 NEON 加载用户地址——硬件会将内核态访问视为具有内核特权,绕过用户地址校验机制
- 异常修复机制(exception fixup)无法正确工作
- 无法处理用户页未映射时的缺页异常
2.4 障碍 3:page fault 处理复杂
copy_from_user 访问非法用户地址时,需要精确跳到修复标签,返回未拷贝的字节数。NEON 指令是多元素加载,遇到页错误时:
- 无法精确定位哪些元素已经加载、哪些未加载
- ARM 架构没有为 NEON 的未对齐/多页访问提供像 GPR 那样的精确异常恢复语义
三、为什么 SVE 更不能用于 uaccess
SVE 除了继承 NEON 的所有问题外,还有额外负担:
| 问题 | SVE 更严重的程度 |
|---|---|
| 上下文开销 | SVE 保存 32×Z 寄存器(VL=128~2048 bit),最大 8KB+,比 NEON 大一个数量级 |
| 抢占延迟 | SVE 长向量操作可能导致内核不可抢占时间显著增加 |
| 页错误处理 | SVE 的 gather/scatter 指令(LD1FF、LDNT1)单条指令跨多页,异常恢复极为复杂 |
| 无社区支持 | 内核社区明确反对在内核态使用 SVE 做数据搬移 |
当前主线内核 没有任何代码路径 在内核态使用 SVE 指令进行用户空间数据访问。
四、ARM 官方推荐的硬件加速方案:MOPS(ARMv8.8+)
ARMv8.8 引入了 MOPS (Memory Operations) 指令集,专门解决内存拷贝的硬件加速问题:
| 操作 | copy_to_user 使用 | copy_from_user 使用 | copy_page 使用 |
|---|---|---|---|
| CPYP (prologue) | cpyfpwt (write+test) |
cpyfprt (read+test) |
cpypwn |
| CPYM (main) | cpyfmwt |
cpyfmrt |
cpymwn |
| CPYE (epilogue) | cpyfewt |
cpyfert |
cpyewn |
MOPS 相比 NEON/SVE 的优势:
| 比较项 | MOPS | NEON | SVE |
|---|---|---|---|
| 上下文开销 | 零(使用 GPR) | 高(需保存 32 regs) | 极高 |
| unprivileged 变体 | ✅ 有 wt/rt 后缀 |
❌ 无 | ❌ 无 |
| 异常恢复 | ✅ 精确 | ❌ 不精确 | ❌ 更差 |
| 内核主线已采用 | ✅ v6.5+ | ❌ | ❌ |
内核中的切换逻辑(arch/arm64/lib/copy_template.S):
#ifdef CONFIG_AS_HAS_MOPS
alternative_if_not ARM64_HAS_MOPS
b .Lno_mops
alternative_else_nop_endif
// copy_to_user 路径
cpyfpwt [dst]!, [src]!, count!
cpyfmwt [dst]!, [src]!, count!
cpyfewt [dst]!, [src]!, count!
b .Lexitfunc
.Lno_mops:
#endif
五、如果只需要内核内部 memcpy,NEON 可以吗?
如果仅仅做内核空间内部(kernel→kernel)的大块内存拷贝,技术上 NEON 可以加速,但实际情况是:
| 场景 | 是否推荐 NEON | 原因 |
|---|---|---|
| 小拷贝 (≤4KB) | ❌ | NEON 上下文开销占比过大 |
| 中拷贝 (4KB-64KB) | ⚠️ 勉强可用 | 收益不确定 |
| 大拷贝 (>64KB) | ❌ | 带宽瓶颈在内存子系统而非计算;ldp/stp 足够饱和 |
| 有 cache 预热的流拷贝 | ❌ | 应使用 DC ZVA + ldp/stp,而非 NEON |
kernel→kernel 的 memcpy 同样不存在页错误和权限区分的顾虑,所以障碍 2 和 3 不存在,但障碍 1(上下文切换开销)依然存在,因此社区同样没有采用 NEON 加速通用 memcpy。
六、总结
| 需求 | 推荐方案 | 内核是否使用 |
|---|---|---|
copy_to_user / copy_from_user 加速 |
MOPS (ARMv8.8+) | ✅ 已合并主线(v6.5+) |
| 内核内部 memcpy 加速 | MOPS 或 GPR ldp/stp |
✅ MOPS 已采用;NEON ❌ |
copy_page |
MOPS 或 GPR ldp/stp |
✅ MOPS 已采用 |
| cache 清理拷贝 | DC CVAC + ldp/stp 或 NT load |
现有实现 |
结论:NEON 和 SVE 不能用于加速 copy_to_user/copy_from_user,原因有三:
kernel_neon_begin/end的上下文保存开销太大(0.5-50 µs),小数据时超过拷贝本身- NEON/SVE 没有 unprivileged load/store 变体,无法区分用户/内核空间访问
- NEON/SVE 多元素加载的页错误恢复语义不精确,无法正确实现
copy_from_user的部分拷贝语义
ARMv8.8 MOPS 指令(主线内核 v6.5 起对 ARM64_HAS_MOPS 的支持)才是当前及未来 ARM 平台内核 uaccess 路径的硬件加速方向。
本站内容均由 AI 基于公开知识辅助生成,仅供学习参考,请勿直接引用作为依据。作者不对信息的准确性、完整性及适用性作保证,亦不对因使用本站内容产生的任何损失承担责任。