“&”与“&&”表达式的 C++ 编译标准?

C++ Compilation Standard for “&” vs “&&” Expressions?

我有一个核心函数,用于评估与 return 布尔值的 4+ 个简单算术比较。这将在一个非常大的循环中调用 O(N^2) 次,具有基于 return.

的单个条件分支

如果函数写成:

    return x < y && g < h && m < n && q < r;

与使用“&”的0相比,它会产生3个条件分支吗?此代码将公开发布,因此可以在具有许多不同编译器实现的许多不同平台上编译。

虽然单个实现可能足以优化短路,但是否将类似的内容写入标准(c++11、14、17 或 20)?仅使用“&”对性能来说是否“更安全”?

我已经提供了 4 个您的代码示例,但在 Godbolt 上有一些注意事项。

  • 无副作用
  • 普通类型
#include <stdint.h>
#include <xmmintrin.h>

bool Test1(int x, int y, int g, int h, int m, int n, int q, int r) {
  return x < y && g < h && m < n && q < r;
}
bool Test2(int x, int y, int g, int h, int m, int n, int q, int r) {
  const bool a = x < y && g < h;
  const bool b = m < n && q < r;
  return a && b;
}
bool TestSIMD(__m128i v1, __m128i v2) {
  __m128i vcmp = _mm_cmplt_epi32(v1, v2);
  uint16_t mask = _mm_movemask_epi8(vcmp);
  return (mask == 0xffff);
}
bool Test4(int x, int y, int g, int h, int m, int n, int q, int r) {
  return x < y & g < h & m < n & q < r;
}

编译为

Test1(int, int, int, int, int, int, int, int):
        cmp     edi, esi
        setl    al
        cmp     edx, ecx
        setl    dl
        and     al, dl
        je      .L1
        cmp     r8d, r9d
        mov     ecx, DWORD PTR [rsp+16]
        setl    al
        cmp     DWORD PTR [rsp+8], ecx
        setl    dl
        and     eax, edx
.L1:
        ret
Test2(int, int, int, int, int, int, int, int):
        cmp     r8d, r9d
        mov     r10d, DWORD PTR [rsp+16]
        setl    al
        cmp     DWORD PTR [rsp+8], r10d
        setl    r8b
        and     eax, r8d
        cmp     edx, ecx
        setl    dl
        and     eax, edx
        cmp     edi, esi
        setl    dl
        and     eax, edx
        ret
TestSIMD(long long __vector(2), long long __vector(2)):
        vpcmpgtd        xmm1, xmm1, xmm0
        vpmovmskb       eax, xmm1
        cmp     ax, -1
        sete    al
        ret
Test4(int, int, int, int, int, int, int, int):
        cmp     r8d, r9d
        mov     r10d, DWORD PTR [rsp+16]
        setl    al
        cmp     DWORD PTR [rsp+8], r10d
        setl    r8b
        and     eax, r8d
        cmp     edx, ecx
        setl    dl
        and     eax, edx
        cmp     edi, esi
        setl    dl
        and     eax, edx
        ret

周期时间是近似值,因为我没有费心分析每条指令。

  • 第一种情况有一个条件分支,如果每次都没有正确预测分支就可以bad。采用 2(早期正确预测)、3,4 或 2+12(分支预测错误)。由于数据微不足道且没有副作用,编译器对短路采取了随意的态度。
  • 第二种情况没有分支,但与第一种情况一样需要 3 或 4 个周期。但是每次的执行时间应该是一样的。
  • 由于数据依赖,SIMD 解决方案需要 4 个周期,没有分支。但是使用的流水线少得多,因此可以与更多指令重叠。此外,数据必须可加载到寄存器中或已经存在于寄存器中,这至少需要一个额外的周期。
  • & 解决方案也需要 4 个周期,但也使用 13 条指令。

因此,如果这接近您的问题,请使用 SIMD,如果您可以在您的平台上使用最快的 Test2 和 Test4。