为什么std :: equal比两个小std :: array的手动循环慢得多?

时间:2016-09-01 03:59:03

标签: c++ performance stl c++14 gcc6

我正在分析一小段代码,这是一个更大的模拟的一部分,令我惊讶的是,STL函数等于(std :: equal)比一个简单的for循环慢得多,比较两个数组元素元件。我写了一个小测试用例,我认为这是两者之间的公平比较,而差异,使用Debian档案中的g ++ 6.1.1并不是无关紧要的。我正在比较有符号整数的两个四元素数组。我测试了std :: equal,operator ==和一个小的for循环。我没有使用std :: chrono进行精确计时,但可以通过时间明确看出差异./a.out。

我的问题是,给定下面的示例代码,为什么operator ==和重载函数std :: equal(调用operator ==我相信)需要大约40秒来完成,而手写循环只需要8s?我正在使用最新的基于英特尔的笔记本电脑。 for循环在所有优化级别上都更快,-O1,-O2,-O3和-Ofast。我编译了代码 g++ -std=c++14 -Ofast -march=native -mtune=native

Run the code

循环运行了很多次,只是为了让肉眼看清楚。模运算符表示对其中一个数组元素的廉价操作,并用于防止编译器优化循环。

#include<iostream>
#include<algorithm>
#include<array>

using namespace std;
using T = array<int32_t, 4>;

bool 
are_equal_manual(const T& L, const T& R)
noexcept {
    bool test{ true };
    for(uint32_t i{0}; i < 4; ++i) { test = test && (L[i] == R[i]); }
    return test;
}

bool
are_equal_alg(const T& L, const T& R)
noexcept {
    bool test{ equal(cbegin(L),cend(L),cbegin(R)) };
    return test;
}

int main(int argc, char** argv) {

    T left{ {0,1,2,3} };
    T right{ {0,1,2,3} };

    cout << boolalpha << are_equal_manual(left,right) << endl;
    cout << boolalpha << are_equal_alg(left,right) << endl;
    cout << boolalpha << (left == right) << endl;

    bool t{};
    const size_t N{ 5000000000 };
    for(size_t i{}; i < N; ++i) {
      //t = left == right; // SLOW
      //t = are_equal_manual(left,right); // FAST
        t = are_equal_alg(left,right);  // SLOW
      left[0] = i % 10;
      right[2] = i % 8;
    }

    cout<< boolalpha << t << endl;

    return(EXIT_SUCCESS);
}

1 个答案:

答案 0 :(得分:1)

以下是使用for函数时main()are_equal_manual(left,right)循环的生成程序集:

.L21:
        xor     esi, esi
        test    eax, eax
        jne     .L20
        cmp     edx, 2
        sete    sil
.L20:
        mov     rax, rcx
        movzx   esi, sil
        mul     r8
        shr     rdx, 3
        lea     rax, [rdx+rdx*4]
        mov     edx, ecx
        add     rax, rax
        sub     edx, eax
        mov     eax, edx
        mov     edx, ecx
        add     rcx, 1
        and     edx, 7
        cmp     rcx, rdi

以下是使用are_equal_alg(left,right)函数时生成的内容:

.L20:
        lea     rsi, [rsp+16]
        mov     edx, 16
        mov     rdi, rsp
        call    memcmp
        mov     ecx, eax
        mov     rax, rbx
        mov     rdi, rbx
        mul     r12
        shr     rdx, 3
        lea     rax, [rdx+rdx*4]
        add     rax, rax
        sub     rdi, rax
        mov     eax, ebx
        add     rbx, 1
        and     eax, 7
        cmp     rbx, rbp
        mov     DWORD PTR [rsp], edi
        mov     DWORD PTR [rsp+24], eax
        jne     .L20

我不确定第一种情况的生成代码中发生了什么,但它显然没有调用memcmp()。它似乎根本没有比较数组的内容。虽然循环仍在迭代50亿次,但它已经过优化,无需进行任何操作。但是,使用are_equal_alg(left,right)的循环仍在执行比较。基本上,编译器仍然能够比std::equal模板更好地优化手动比较。