Tob*_*ann 5 c++ optimization loops compiler-optimization
我试着理解在什么情况下C++编译器能够执行循环融合,何时不能.
以下代码测量两种不同方法的性能,以计算f(x) = (2*x)^2向量中所有值的平方双精度().
#include <chrono>
#include <iostream>
#include <numeric>
#include <vector>
constexpr int square( int x )
{
return x * x;
}
constexpr int times_two( int x )
{
return 2 * x;
}
// map ((^2) . (^2)) $ [1,2,3]
int manual_fusion( const std::vector<int>& xs )
{
std::vector<int> zs;
zs.reserve( xs.size() );
for ( int x : xs )
{
zs.push_back( square( times_two( x ) ) );
}
return zs[0];
}
// map (^2) . map (^2) $ [1,2,3]
int two_loops( const std::vector<int>& xs )
{
std::vector<int> ys;
ys.reserve( xs.size() );
for ( int x : xs )
{
ys.push_back( times_two( x ) );
}
std::vector<int> zs;
zs.reserve( ys.size() );
for ( int y : ys )
{
zs.push_back( square( y ) );
}
return zs[0];
}
template <typename F>
void test( F f )
{
const std::vector<int> xs( 100000000, 42 );
const auto start_time = std::chrono::high_resolution_clock::now();
const auto result = f( xs );
const auto end_time = std::chrono::high_resolution_clock::now();
const auto elapsed = end_time - start_time;
const auto elapsed_us = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count();
std::cout << elapsed_us / 1000 << " ms - " << result << std::endl;
}
int main()
{
test( manual_fusion );
test( two_loops );
}
Run Code Online (Sandbox Code Playgroud)
具有两个循环的版本花费的时间大约是具有一个循环的版本的两倍,即使-O3对于GCC和Clang也是如此.
有没有一种办法可以让编译器优化two_loops应运而生一样快,manual_fusion而不会在第二循环中就地操作?我问的原因是我想对我的图书馆链式调用FunctionalPlus一样fplus::enumerate(fplus::transform(f, xs));快.
您可以尝试修改two_loops函数,如下所示:
int two_loops( const std::vector<int>& xs )
{
std::vector<int> zs;
zs.reserve( xs.size() );
for ( int x : xs )
{
zs.push_back( times_two( x ) );
}
for ( int i=0 : i<zs.size(); i++ )
{
zs[i] = ( square( zs[i] ) );
}
return zs[0];
}
Run Code Online (Sandbox Code Playgroud)
重点是避免分配内存两次并将push_back到另一个向量