ead*_*ead 9 c++ optimization gcc clang
在我们的代码库中,我们有很多操作,如j*ω*X,其中j是虚数单位,ω是实数,X是复数.实际上很多循环看起来像:
#include <complex>
#include <vector>
void mult_jomega(std::vector<std::complex<double> > &vec, double omega){
std::complex<double> jomega(0.0, omega);
for (auto &x : vec){
x*=jomega;
}
}
Run Code Online (Sandbox Code Playgroud)
但是,我们利用了真实部分jomega为零的事实并将乘法写为:
void mult_jomega_smart(cvector &vec, double omega){
for (auto &x : vec){
x={-omega*x.imag(), omega*x.real()};
}
}
Run Code Online (Sandbox Code Playgroud)
一开始,我对这种"聪明"的转变不屑一顾,因为
然而,正如一些表现回归所显示的那样,第三个论点并不成立.当比较这两种功能(见下文列表),智能版采用一贯优于-O2以及具有-O3:
size orig(musec) smart(musec) speedup
10 0.039928 0.0117551 3.39665
100 0.328564 0.0861379 3.81439
500 1.62269 0.417475 3.8869
1000 3.33012 0.760515 4.37877
2000 6.46696 1.56048 4.14422
10000 32.2827 9.2361 3.49528
100000 326.828 115.158 2.8381
500000 1660.43 850.415 1.95249
Run Code Online (Sandbox Code Playgroud)
智能版本在我的机器上快了大约4倍(gcc-5.4),并且随着任务变得越来越多,随着阵列尺寸的增加,内存受限,加速下降到因子2.
我的问题是,什么阻止编译器优化不太智能但更易读的版本,毕竟编译器可以看到,实际部分jomega是零?是否可以通过提供一些额外的编译标志来帮助编译器进行优化?
注意:其他编译器也存在加速:
compiler speedup
g++-5.4 4
g++-7.2 4
clang++-3.8 2 [original version 2-times faster than gcc]
Run Code Online (Sandbox Code Playgroud)
人数:
mult.cpp - 防止内联:
#include <complex>
#include <vector>
typedef std::vector<std::complex<double> > cvector;
void mult_jomega(cvector &vec, double omega){
std::complex<double> jomega(0.0, omega);
for (auto &x : vec){
x*=jomega;
}
}
void mult_jomega_smart(cvector &vec, double omega){
for (auto &x : vec){
x={-omega*x.imag(), omega*x.real()};
}
}
Run Code Online (Sandbox Code Playgroud)
main.cpp中:
#include <chrono>
#include <complex>
#include <vector>
#include <iostream>
typedef std::vector<std::complex<double> > cvector;
void mult_jomega(cvector &vec, double omega);
void mult_jomega2(cvector &vec, double omega);
void mult_jomega_smart(cvector &vec, double omega);
const size_t N=100000; //10**5
const double OMEGA=1.0;//use 1, so nothing changes -> no problems with inf & Co
void compare_results(const cvector &vec){
cvector m=vec;
cvector m_smart=vec;
mult_jomega(m, 5.0);
mult_jomega_smart(m_smart,5.0);
std::cout<<m[0]<<" vs "<<m_smart[0]<<"\n";
std::cout<< (m==m_smart ? "equal!" : "not equal!")<<"\n";
}
void test(size_t vector_size){
cvector vec(vector_size, std::complex<double>{1.0, 1.0});
//compare results, triger if in doubt
//compare_results(vec);
//warm_up, just in case:
for(size_t i=0;i<N;i++)
mult_jomega(vec, OMEGA);
//test mult_jomega:
auto begin = std::chrono::high_resolution_clock::now();
for(size_t i=0;i<N;i++)
mult_jomega(vec, OMEGA);
auto end = std::chrono::high_resolution_clock::now();
auto time_jomega=std::chrono::duration_cast<std::chrono::nanoseconds>(end-begin).count()/1e3;
//test mult_jomega_smart:
begin = std::chrono::high_resolution_clock::now();
for(size_t i=0;i<N;i++)
mult_jomega_smart(vec, OMEGA);
end = std::chrono::high_resolution_clock::now();
auto time_jomega_smart=std::chrono::duration_cast<std::chrono::nanoseconds>(end-begin).count()/1e3;
double speedup=time_jomega/time_jomega_smart;
std::cout<<vector_size<<"\t"<<time_jomega/N<<"\t"<<time_jomega_smart/N<<"\t"<<speedup<<"\n";
}
int main(){
std::cout<<"N\tmult_jomega(musec)\tmult_jomega_smart(musec)\tspeedup\n";
for(const auto &size : std::vector<size_t>{10,100,500,1000,2000,10000,100000,500000})
test(size);
}
Run Code Online (Sandbox Code Playgroud)
建设和运营:
g++ main.cpp mult.cpp -O3 -std=c++11 -o mult_test
./mult_test
Run Code Online (Sandbox Code Playgroud)
使用标志进行编译可以获得-ffast-math快速的性能.
N mult_jomega(musec) mult_jomega_smart(musec) speedup
10 0.00860809 0.00818644 1.05151
100 0.0706683 0.0693907 1.01841
500 0.29569 0.297323 0.994509
1000 0.582059 0.57622 1.01013
2000 1.30809 1.24758 1.0485
10000 7.37559 7.4854 0.98533
Run Code Online (Sandbox Code Playgroud)
编辑:更具体地说,它是-funsafe-math-optimizations编译器标志.根据文档,这个标志用于
允许优化浮点运算,(a)假设参数和结果有效,(b)可能违反IEEE或ANSI标准.什么时候
编辑2:更具体地说,它是-fno-signed-zeros选项,它是:
允许优化浮点运算,忽略零的符号.IEEE算法指定distinct
+0.0和-0.0values 的行为 ,然后禁止简化表达式,例如x+0.0or或0.0*x(甚至with-ffinite-math-only).此选项意味着零结果的符号不重要.
| 归档时间: |
|
| 查看次数: |
604 次 |
| 最近记录: |