【发布时间】:2017-01-08 19:18:57
【问题描述】:
我正在分析一小段代码,它是更大模拟的一部分,令我惊讶的是,STL 函数 equal (std::equal) 比简单的 for 循环慢得多,比较两个数组元素元素。我写了一个小测试用例,我认为这是两者之间的公平比较,并且使用 Debian 档案中的 g++ 6.1.1 的差异并非微不足道。我正在比较两个有符号整数的四元素数组。我测试了 std::equal、operator== 和一个小的 for 循环。我没有使用 std::chrono 来确定确切的时间,但是可以通过 time ./a.out 明确地看到差异。
我的问题是,鉴于下面的示例代码,为什么 operator== 和重载函数 std::equal(我相信它调用 operator==)需要大约 40 秒才能完成,而手写循环只需要 8 秒?我正在使用最近的基于英特尔的笔记本电脑。 for 循环在所有优化级别(-O1、-O2、-O3 和 -Ofast)上都更快。我编译了代码
g++ -std=c++14 -Ofast -march=native -mtune=native
循环运行了很多次,只是为了让肉眼清楚地看到差异。模运算符表示对数组元素之一的廉价操作,用于防止编译器在循环外进行优化。
#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);
}
【问题讨论】:
-
Assembly output,手动比较是展开 cmpl,而
equal和==使用的是memcmp -
如果你传递你自己的 lambda
bool test{ equal(cbegin(L),cend(L),cbegin(R), [](int a, int b) { return a == b; })};编译器似乎可以更好地优化它。 -
Jesse - 在这种情况下必须传递一个 lambda 对我来说似乎有点多余;这不是 operator== 应该做的,因为它们是基本类型吗?
-
libstdc++ 的
equaldispatches tomemcmpwhen it's safe。这是一种优化,但在这种情况下,这可能不是正确的做法。 -
@KBentley57:是的,你是对的。正如 T.C. 所指出的,这是 libstdc++ 库中的一个实现问题
标签: c++ performance stl c++14 gcc6