为什么基于范围的for()循环比std::transform()更快?
为什么基于范围的for()循环比std::transform()更快?
嘿,我最近碰到个挺疑惑的事儿,本来以为下面这两段代码跑起来性能应该差不离,但在我的硬件上实测下来,基于范围的for()循环只花了大概80毫秒,而std::transform()却要120毫秒左右,这差距还挺明显的。
先把我测试的代码贴出来,方便大家参考:
#include <algorithm> #include <iostream> #include <chrono> int main() { const auto t1 = std::chrono::high_resolution_clock::now(); /* When using loop #1, this code takes 80 milliseconds to run. * When using loop #2, this code takes 120 milliseconds to run. */ // 循环#1:基于范围的for循环 // for (auto& elem : my_container) { // elem = some_operation(elem); // } // 循环#2:std::transform // std::transform(my_container.begin(), my_container.end(), my_container.begin(), // [](auto elem) { return some_operation(elem); }); const auto t2 = std::chrono::high_resolution_clock::now(); const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count(); std::cout << "Time taken: " << duration << "ms" << std::endl; return 0; }
那为啥会有这种性能差异呢?我琢磨了下,大概有这几个可能的原因:
- 编译器优化的「透明度」不一样:范围for循环是C++11之后的语法糖,编译器对它的处理非常直接——对于像
std::vector这种连续容器,它会直接转换成指针式的遍历,逻辑简单明了,编译器能轻松做激进的优化,比如循环展开、消除边界检查。而std::transform()是标准库的模板函数,它要适配各种迭代器类型和函数对象,编译器在处理的时候,可能没法像看透手写循环那样完全拆解整个逻辑,导致优化没那么彻底。 - 迭代器的细微开销:虽然标准库的迭代器大多会被优化成裸指针,但某些情况下,迭代器的类型推导、边界检查(比如调试模式下)或者额外的封装逻辑,可能会带来一点点额外的开销。而范围for循环直接绕开了这些,遍历过程更「直白」。
- 函数调用的间接性:如果在
std::transform()里用了lambda或者自定义仿函数,哪怕编译器会尝试inline这个调用,但毕竟多了一层函数对象的传递和调用逻辑,和范围for里直接把操作写在循环体里比,还是可能有细微的差异。手写循环里的操作是直接内联在循环里的,没有这层间接性。 - 编译器和优化等级的影响:不同的编译器(比如GCC、Clang、MSVC)对标准库的实现细节不一样,对
std::transform()的优化力度也有区别。甚至同一个编译器的不同版本,优化效果都可能不同。另外一定要确保你是用相同的优化等级编译的(比如都是-O2或者-O3),如果一个开了-O3另一个只开了-O1,那结果肯定没可比性。
不过说句实在的,大多数生产环境下,这两者的性能差异其实非常小,甚至很多时候编译器能把std::transform()优化得和手写循环一样快。你遇到的这种明显差异,大概率是特定硬件、编译器版本或者代码里的一些细节(比如目标容器是否提前分配好空间?有没有额外的拷贝?)导致的。
备注:内容来源于stack exchange,提问作者Stéphane
相关产品推荐
相关产品推荐

