why is c++ std::max_element so slow?
我需要在向量中找到max元素,所以我使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #include <string> #include <iostream> #include <vector> #include #include <sys/time.h> double getRealTime() { struct timeval tv; gettimeofday(&tv, 0); return (double) tv.tv_sec + 1.0e-6 * (double) tv.tv_usec; } inline int my_max_element(const std::vector<int> &vec, int size) { auto it = vec.begin(); int max = *it++; for (; it != vec.end(); it++) { if (*it > max) { max = *it; } } return max; } int main() { const int size = 1 << 20; std::vector<int> vec; for (int i = 0; i < size; i++) { if (i == 59) { vec.push_back(1000000012); } else { vec.push_back(i); } } double startTime = getRealTime(); int maxIter = *std::max_element(vec.begin(), vec.end()); double stopTime = getRealTime(); double totalIteratorTime = stopTime - startTime; startTime = getRealTime(); int maxArray = my_max_element(vec, size); stopTime = getRealTime(); double totalArrayTime = stopTime - startTime; std::cout <<"MaxIter =" << maxIter << std::endl; std::cout <<"MaxArray =" << maxArray << std::endl; std::cout <<"Total CPU time iterator =" << totalIteratorTime << std::endl; std::cout <<"Total CPU time array =" << totalArrayTime << std::endl; std::cout <<"iter/array ratio: =" << totalIteratorTime / totalArrayTime << std::endl; return 0; } |
输出:
1 2 3 4 5 | MaxIter = 1000000012 MaxArray = 1000000012 Total CPU time iterator = 0.000989199 Total CPU time array = 0.000293016 iter/array ratio: = 3.37592 |
平均来说,
注意:起初我认为这是因为我在for循环中使用和整数
编译信息:
G+(GCC)4.8
G++-O3-墙-C-FEMIT长度=0 -STD= C++0X
在对该答案进行投票之前,请在您的计算机上测试(并验证)并评论/添加结果。注意,我的测试使用了1000*1000*1000的向量大小。目前,这个答案有19个赞成票,但只有一个公布的结果,这些结果没有显示下面描述的效果(虽然获得了不同的测试代码,见评论)。
似乎存在优化器错误/工件。比较以下时间:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | template<typename _ForwardIterator, typename _Compare> _ForwardIterator my_max_element_orig(_ForwardIterator __first, _ForwardIterator __last, _Compare __comp) { if (__first == __last) return __first; _ForwardIterator __result = __first; while(++__first != __last) if (__comp(__result, __first)) __result = __first; return __result; } template<typename _ForwardIterator, typename _Compare> _ForwardIterator my_max_element_changed(_ForwardIterator __first, _ForwardIterator __last, _Compare __comp) { if (__first == __last) return __first; _ForwardIterator __result = __first; ++__first; for(; __first != __last; ++__first) if (__comp(__result, __first)) __result = __first; return __result; } |
第一个是最初的libstdc++实现,第二个应该是一个没有任何行为或需求变化的转换。clang++为这两个函数生成非常相似的运行时间,而G++4.8.2的运行速度是第二个版本的四倍。
根据Maxim的建议,将矢量从
区别在于
1 2 3 4 5 6 7 8 | w/o commoning with commoning * * ** * ** * ** * * * * * * * * * * |
下面是用于比较的asm(
使用while循环(2.88743 ms;gist):
1 2 3 4 5 6 7 8 9 10 | movq %rdi, %rax jmp .L49 .L51: movl (%rdi), %edx cmpl %edx, (%rax) cmovl %rdi, %rax .L49: addq $4, %rdi cmpq %rsi, %rdi jne .L51 |
对于for循环(1235.55μs):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | leaq 4(%rdi), %rdx movq %rdi, %rax cmpq %rsi, %rdx je .L53 movl (%rdi), %ecx .L54: movl (%rdx), %r8d cmpl %r8d, %ecx cmovl %rdx, %rax cmovl %r8d, %ecx addq $4, %rdx cmpq %rdx, %rsi jne .L54 .L53: |
如果在开始时和更新
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | movl (%rdi), %ecx movq %rdi, %rax .L57: addq $4, %rdi cmpq %rsi, %rdi je .L60 .L59: movl (%rdi), %edx cmpl %edx, %ecx jge .L57 movq %rdi, %rax addq $4, %rdi movl %edx, %ecx cmpq %rsi, %rdi jne .L59 .L60: |
这比
您可能在64位模式下运行测试,其中
如果将
1 2 3 4 5 | MaxIter = 1000000012 MaxArray = 1000000012 Total CPU time iterator = 0.00429082 Total CPU time array = 0.00572205 iter/array ratio: = 0.749875 |
一个重要的注意事项是:当基准测试禁用CPU频率缩放时,CPU不会在基准测试中间切换档位。
但我认为这里还有其他一些因素在起作用,因为仅仅将循环变量从
这是缓存的一个简单问题。也就是说,第一次加载内存时,在本例中是向量的内容,它总是比最近访问时慢得多。我用GCC4.9复制并粘贴了您的代码。
当函数反转时,比率为1。当它们按原始顺序排列时,比率为1.6。
对于我来说,在max_元素的情况下,这仍然是gcc的一个基本错误优化。但是,您的功能时间非常短,它们将由CPU噪声控制,如上面的缓存效果,而不是任何有意义的比较。
反转,原始