首先,您可能使用GCC作为编译器,它使用asm内联汇编程序的语句.使用它时,您必须使用字符串文字作为汇编代码(在发送到汇编程序之前将其复制到汇编代码中 - 这意味着该字符串应包含换行符).
其次,您可能必须使用AT&T语法进行汇编.
第三个GCC使用扩展的asm在汇编程序和C之间传递变量.
第四,你应该尽可能避免使用内联汇编程序,因为编译器不可能通过asm语句安排指令(至少这是真的).相反,你可以使用像vector_size属性这样的GCC扩展:
typedef float v4sf __attribute__((vector_size(16)));
void fubar( v4sf *p, v4sf* q )
{
v4sf p0 = *p++;
v4sf p1 = *p++;
v4sf p2 = *p++;
v4sf p3 = *p++;
*q++ = p0;
*q++ = p1;
*q++ = p2;
*q++ = p3;
}
Run Code Online (Sandbox Code Playgroud)
有一个优点是,即使你为没有mmx寄存器的处理器编译,编译器也会生成代码,但也许是其他一些128位寄存器(或根本没有向量寄存器).
第五,你应该调查提供的memcpy是否不够快.通常memcpy真的是优化的.
第六,如果你在Linux内核中使用特殊寄存器,你应该采取预防措施,有些寄存器在上下文切换期间没有保存.SSE寄存器是其中的一部分.
第七,当您使用它来测试吞吐量时,您应该考虑处理器是否是等式中的重要瓶颈.将代码的实际执行与RAM的读/写(你是否命中缓存?)或从/写到外设的读取进行比较.
第八,当移动数据时,你应该避免将大块数据从RAM移动到RAM,如果它是来自/来自带宽有限的外设,你应该考虑使用DMA.请记住,如果它的访问时间限制了性能,CPU仍将被视为忙碌(尽管它不能以100%的速度运行).
暂时将这个答案留在这里,尽管现在很明显 OP 只想要一次16B 传输。在 Linux 上,他的代码导致 PCIe 总线上的两次 8B 传输。
对于写入 MMIO 空间,值得尝试movntiwrite-combining-store 指令。的源操作数movnti是 GP 寄存器,而不是向量 reg。
如果您#include <immintrin.h>在驱动程序代码中,您可能可以使用内在函数生成它。只要您小心使用的内在函数,这在内核中应该没问题。它没有定义任何全局变量。
所以本节的大部分内容都不是很相关。
在大多数 CPU 上(哪里rep movs好),Linux 的 memcpy 使用它。它仅使用回退到显式循环的 CPU,其中rep movsq或rep movsb不是好的选择。
当大小是编译时常量时,memcpy 有一个使用rep movsl(AT&T 语法 for rep movsd)的内联实现,然后用于清理:非rep movsw和movsb如果需要。(其实有点笨重,国际海事组织,因为大小是一个编译时间常数。也没有利用快rep movsb上有它的CPU。)
自 P6 以来的 Intel CPU 至少有相当好的rep movs实现。请参阅Andy Glew 对此的评论。
但是,您仍然错误地认为 memcpy 只在 64 位块中移动,除非我误读了代码,或者您所在的平台决定使用回退循环。
无论如何,我认为您使用普通 Linux 并不会错过太多性能memcpy,除非您实际上单步执行了代码并看到它做了一些愚蠢的事情。
对于大型副本,您无论如何都需要设置 DMA。驱动程序的 CPU 使用率很重要,而不仅仅是在空闲系统上可以获得的最大吞吐量。(小心不要过于相信微基准测试。)
在内核中使用 SSE 意味着保存/恢复向量寄存器。对于 RAID5/RAID6 代码来说,这是值得的。该代码只能从专用线程运行,而不是从向量/FPU 寄存器仍具有另一个进程数据的上下文中运行。
Linux 的 memcpy 可以在任何上下文中使用,因此它避免使用除通常的整数寄存器之外的任何内容。我确实找到了一篇关于 SSE 内核 memcpy 补丁的文章,其中 Andi Kleen 和 Ingo Molnar 都说总是将 SSE 用于 memcpy 是不好的。也许对于大副本可能有一个特殊的bulk-memcpy,值得保存向量regs。
您可以在内核中使用 SSE,但您必须将其包装在kernel_fpu_begin()和 中kernel_fpu_end()。在 Linux 3.7 及更高版本上,kernel_fpu_end() 实际上执行恢复 FPU 状态的工作,因此不要在函数中使用大量 fpu_begin/fpu_end 对。另请注意, kernel_fpu_begin 禁用抢占,您不得“做任何可能出错或休眠的事情”。
理论上,只保存一个向量 reg,比如 xmm0,会很好。您必须确保使用 SSE,而不是AVX 指令,因为您需要避免将 ymm0 / zmm0 的上部归零。当您返回使用 ymm regs 的代码时,您可能会导致 AVX+SSE 停顿。除非您想完全保存矢量 regs,否则您不能运行 vzeroupper。甚至要做到这一点,你需要检测 AVX 支持......
但是,即使执行此单寄存器保存/恢复操作也需要您采取与 相同的预防措施kernel_fpu_begin,并禁用抢占。由于您将存储到您自己的私人保存槽(可能在堆栈上),而不是 to task_struct.thread.fpu,我不确定即使禁用抢占也足以保证用户空间 FPU 状态不会被破坏. 也许是,但也许不是,而且我不是内核黑客。禁用中断来防止这种情况也可能比仅kernel_fpu_begin()/kernel_fpu_end()使用 XSAVE/XRSTOR 触发完整的 FPU 状态保存更糟糕。
| 归档时间: |
|
| 查看次数: |
2001 次 |
| 最近记录: |