【发布时间】:2014-04-08 17:48:13
【问题描述】:
我正在尝试优化以下函数(简化了一点,但这是我的程序花费大量时间的循环):
int f(int len, unsigned char *p) {
int i = 0;
while (i < len && p[i] >= 32 && p[i] <= 127) {
i++;
}
return i;
}
我认为它可以使用向量指令进行优化,但通过一些研究,看起来 SSE 并不适合在字节级别工作。该程序仅针对 OSX 上的 64 位 Intel CPU。是否有一个我没有看到的聪明的位技巧可以让我一次处理 64 位?带有 -O3 的 llvm 没有进行任何巧妙的优化。
更新:
SIMD 代码在我的基准测试中通常是最快的(取决于输入的大小),但由于某种原因,使用 SIMD 的应用程序总体上比使用幼稚代码或位旋转技巧的应用程序要慢。就上下文而言,该应用程序在终端仿真器的输入流中查找 ASCII 字符串子序列的长度。 ASCII 字符串得到特殊的“快速路径”处理。我只能将一个答案标记为正确,但两者都很棒。我确实对位旋转做了一个小改进,通过这样做删除了一个 if 语句:
while (i < len - 8) {
uint64_t bytes = *(uint64_t *)(p + i);
uint64_t middleBits = bytes & 0x6060606060606060;
uint64_t highBits = bytes & 0x8080808080808080;
middleBits |= (middleBits >> 1);
middleBits &= ~(highBits >> 2);
if ((middleBits & 0x2020202020202020) != 0x2020202020202020) {
break;
}
i += 8;
}
【问题讨论】:
-
64 位 x86 意味着 SSE2,因此它应该使用 16 个 8 位字节。 SSE 没有整数指令。此外,如果它足够新,那么您可以使用 AVX2
-
如果您的数据集足够大,您可以从在循环中抛出 OpenMP pragma 开始。如果您不介意我的询问,您正在处理的数据的具体性质是什么?
-
对任何人的小提示:如果您假装
p[i]是一个签名字符(即使它不是),那么p[i] >= 32 && p[i] <= 127可以简化为p[i] >= 32。 -
对于特定机器的更多优化使用
-march=native -
您的性能问题可能是由多种原因造成的。您可能需要调整 SSE 例程以确保使用对齐的负载。查看生成的汇编程序,以确保有适当的寄存器使用。您可能还会发现您的性能是可变的,具体取决于您为数据分配的内存如何对齐以及如何将其提取到缓存行中。
标签: c++ c optimization simd