我希望现代C ++编译器能够生成最快的机器代码。或者我们还会在2016年坚持使用内联装配吗? 我需要搜索与一个特定框相交的浮点3D边界框。 我对汇编程序版本的想法是
1. cmpps => register A
2. cmpss => register B
3. C = A & B
4. convert C into "normal" register
5. compare it with zero
6. conditionally jump
即使在第4个浮点数的填充结构之后,GCC 4.4和Visual Studio Comunity 2015也只使用comiss指令一次生成一个浮点数比较。是否需要部分C ++表达式顺序,或者这些编译器无法自行优化它?
我的测试用例:
struct Vec
{
float x,y,z,w;
};
struct BBox
{
Vec min,max;
};
bool bbox(BBox& b, Vec& v)
{
return
b.min.x <= v.x && v.x <= b.max.x &&
b.min.y <= v.y && v.y <= b.max.y &&
b.min.z <= v.z && v.z <= b.max.z &&
b.min.w <= v.w && v.w <= b.max.w;
}
int main()
{
BBox b;
Vec v;
return bbox(b, v);
}
答案 0 :(得分:4)
太糟糕了,gcc / clang不会自动进行此操作,因为它是pretty easy:
// clang doesn't let this be a constexpr to mark it as a pure function :/
const bool bbox(const BBox& b, const Vec& v)
{
// if you can guarantee alignment, then these can be load_ps
// saving a separate load instruction for SSE. (AVX can fold unaligned loads)
// maybe make Vec a union with __m128
__m128 blo = _mm_loadu_ps(&b.min.x);
__m128 bhi = _mm_loadu_ps(&b.max.x);
__m128 vv = _mm_loadu_ps(&v.x);
blo = _mm_cmple_ps(blo, vv);
bhi = _mm_cmple_ps(vv, bhi);
__m128 anded = _mm_and_ps(blo, bhi);
int mask = _mm_movemask_ps(anded);
// mask away the result from the padding element,
// check that all the bits are set
return (mask & 0b0111) == 0b0111;
}
这将编译为
movups xmm0, xmmword ptr [rdi]
movups xmm1, xmmword ptr [rdi + 16]
movups xmm2, xmmword ptr [rsi]
cmpleps xmm0, xmm2
cmpleps xmm2, xmm1
andps xmm2, xmm0
movmskps eax, xmm2
and eax, 7
cmp eax, 7
sete al
ret
如果你颠倒了比较感(cmpnle),要测试是否在任何轴上的边界框之外,你可以做类似的事情
int mask1 = _mm_movemask_ps(blo);
int mask2 = _mm_movemask_ps(bhi);
return !(mask1 | mask2);
可能编译为
movmsk
movmsk
or
setnz
所以整数测试更便宜,你用另一个movmsk替换一个向量AND(大约相等的成本)。
我想了一会儿这样做就意味着NaN算在盒子里面,但实际上当NaN中的一个操作数时cmpnleps是真的。 (在这种情况下,cmpleps是假的,所以它恰恰相反)。
我没有想过在这种情况下填充会发生什么。它可能最终成为!((mask1|mask2) & 0b0111)
,这对于x86来说仍然更有效,因为test
指令可以免费执行AND,并且可以与Intel和AMD上的分支指令进行宏融合。
movmskps是AMD的2 m-ops和高延迟,但使用矢量可能仍然是一个胜利。 AMD上的两条movmskps指令可能比我先发布的代码略差,但它是流水线的,所以它们都可以在cmpps指令完成后进行传输。
答案 1 :(得分:1)
如果你真的想要CMPPS
机器指令,use the __builtin_ia32_cmpps
builtin。
但我认为你可能不需要这样做。 信任您的编译器并要求使用gcc -ffast-math -mcpu=native -O3
进行许多优化;也许添加一些其他optimization flags并考虑链接时优化,例如编译&amp;使用gcc -flto -ffast-math -mcpu=native -O3
如果你有几个星期没有尝试手动调整优化(你的时间如此便宜,值得付出努力?),我建议通过小心和巧妙地调整缓存预取/ em>添加few __builtin_prefetch
(但是,基准与运行lasting more than a second并添加很少的预取!)。
手写和过早优化通常是无用的,适得其反
顺便说一句,编译器编写者正在做一些小而持续的进展。使用最新版本的GCC和Clang/LLVM进行测试可能是一种更好的方式来花时间。不要忘记PC中timing of operations的典型数量级。