【发布时间】:2019-10-07 10:39:58
【问题描述】:
想象一下,我有这个简单的函数来检测球体重叠。这个问题的重点并不是真正讨论对球体进行命中测试的最佳方法,所以这只是为了说明。
inline bool sphere_hit(float x1, float y1, float z1, float r1,
float x2, float y2, float z2, float r2) {
float xd = (x1 - x2);
float yd = (y1 - y2);
float zd = (z1 - z2);
float max_dist = (r1 + r2);
return xd * xd + yd * yd + zd * zd < max_dist * max_dist;
}
我在嵌套循环中调用它,如下:
std::vector<float> xs, ys, zs, rs;
int n_spheres;
// <snip>
int n_hits = 0;
for (int i = 0; i < n_spheres; ++i) {
for (int j = i + 1; j < n_spheres; ++j) {
if (sphere_hit(xs[i], ys[i], zs[i], rs[i],
xs[j], ys[j], zs[j], rs[j])) {
++n_hits;
}
}
}
std::printf("total hits: %d\n", n_hits);
现在,clang(使用-O3 -march=native)足够聪明,可以弄清楚如何将此循环向量化(和展开)为 256 位 avx2 指令。太棒了!
但是,如果我做的事情比增加点击次数更复杂,例如调用某个任意函数 handle_hit(i, j),clang 会发出一个简单的标量版本。
命中应该是非常罕见的,所以我认为应该检查每个矢量化循环迭代,如果 any 车道的值为真,如果是,则跳转到一些标量慢路径。这应该可以使用vcmpltps 后跟vmovmskps。但是,即使我用__builtin_expect(..., 0) 包围对sphere_hit 的调用,我也无法发出此代码。
【问题讨论】:
-
也许将另一个内部循环写入当前的内部循环,该内部循环首先将 8 个
sphere_hit调用的结果写入位集(并希望这会生成一个 @ 987654330@ 指令)可以工作。 (如果 bitset 不为零,则遍历 bitset)。
标签: vectorization simd llvm-clang micro-optimization avx2