Ola*_*a M 27 c performance gcc
我正在测试一个计算Mandelbrot分形的简单代码.我一直在检查它的性能,具体取决于函数中的迭代次数,它检查一个点是否属于Mandelbrot集.令人惊讶的是,在添加-fPIC旗帜后,我的时间差异很大.从我读到的开销通常可以忽略不计,我遇到的最高开销约为6%.我大约30%.任何建议将被认真考虑!
我使用-O3标志,gcc 4.7.2,Ubuntu 12.04.2,x86_64.结果如下
#iter C (fPIC) C C/C(fPIC)
1 0.01 0.01 1.00
100 0.04 0.03 0.75
200 0.06 0.04 0.67
500 0.15 0.1 0.67
1000 0.28 0.19 0.68
2000 0.56 0.37 0.66
4000 1.11 0.72 0.65
8000 2.21 1.47 0.67
16000 4.42 2.88 0.65
32000 8.8 5.77 0.66
64000 17.6 11.53 0.66
我使用的命令:
gcc -O3 -fPIC fractalMain.c fractal.c -o ffpic
gcc -O3 fractalMain.c fractal.c -o f
Run Code Online (Sandbox Code Playgroud)
#include <time.h>
#include <stdio.h>
#include <stdbool.h>
#include "fractal.h"
int main()
{
int iterNumber[] = {1, 100, 200, 500, 1000, 2000, 4000, 8000, 16000, 32000, 64000};
int it;
for(it = 0; it < 11; ++it)
{
clock_t start = clock();
fractal(iterNumber[it]);
clock_t end = clock();
double millis = (end - start)*1000 / CLOCKS_PER_SEC/(double)1000;
printf("Iter: %d, time: %lf \n", iterNumber[it], millis);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
#ifndef FRACTAL_H
#define FRACTAL_H
void fractal(int iter);
#endif
Run Code Online (Sandbox Code Playgroud)
#include <stdio.h>
#include <stdbool.h>
#include "fractal.h"
void multiplyComplex(double a_re, double a_im, double b_re, double b_im, double* res_re, double* res_im)
{
*res_re = a_re*b_re - a_im*b_im;
*res_im = a_re*b_im + a_im*b_re;
}
void sqComplex(double a_re, double a_im, double* res_re, double* res_im)
{
multiplyComplex(a_re, a_im, a_re, a_im, res_re, res_im);
}
bool isInSet(double P_re, double P_im, double C_re, double C_im, int iter)
{
double zPrev_re = P_re;
double zPrev_im = P_im;
double zNext_re = 0;
double zNext_im = 0;
double* p_zNext_re = &zNext_re;
double* p_zNext_im = &zNext_im;
int i;
for(i = 1; i <= iter; ++i)
{
sqComplex(zPrev_re, zPrev_im, p_zNext_re, p_zNext_im);
zNext_re = zNext_re + C_re;
zNext_im = zNext_im + C_im;
if(zNext_re*zNext_re+zNext_im*zNext_im > 4)
{
return false;
}
zPrev_re = zNext_re;
zPrev_im = zNext_im;
}
return true;
}
bool isMandelbrot(double P_re, double P_im, int iter)
{
return isInSet(0, 0, P_re, P_im, iter);
}
void fractal(int iter)
{
int noIterations = iter;
double xMin = -1.8;
double xMax = 1.6;
double yMin = -1.3;
double yMax = 0.8;
int xDim = 512;
int yDim = 384;
double P_re, P_im;
int nop;
int x, y;
for(x = 0; x < xDim; ++x)
for(y = 0; y < yDim; ++y)
{
P_re = (double)x*(xMax-xMin)/(double)xDim+xMin;
P_im = (double)y*(yMax-yMin)/(double)yDim+yMin;
if(isMandelbrot(P_re, P_im, noIterations))
nop = x+y;
}
printf("%d", nop);
}
Run Code Online (Sandbox Code Playgroud)
-fPIC在构建可执行文件时添加标志可能看起来有点人为(根据其中一条注释).所以说几句话:首先我只将程序编译为可执行程序,并希望与我的Lua代码进行比较,后者从C调用isMandelbrot函数.所以我创建了一个共享对象来从lua调用它 - 并且有很大的时间差异.但无法理解为什么他们的迭代次数在增长.最终发现它是因为-fPIC.当我创建一个调用我的lua脚本的小程序时(我有效地做同样的事情,只需要.so) - 时间非常类似于C(没有-fPIC).所以我在过去的几天里用几种配置检查了它,并且它始终显示两组非常相似的结果:没有-fPIC更慢而且速度更慢.
red*_*bit 40
事实证明,当你没有编译的-fPIC选项multiplyComplex,sqComplex,isInSet并且isMandelbrot是由编译器自动内联.如果将这些函数定义为static,则在编译时可能会获得相同的性能,-fPIC因为编译器可以自由执行内联.
编译器无法自动内联辅助函数的原因与符号插入有关.需要位置无关的代码来间接访问所有全局数据,即通过全局偏移表.同样的约束适用于函数调用,函数调用必须通过过程链接表.由于符号可能在运行时被另一个符号插入(请参阅参考资料LD_PRELOAD),因此编译器不能简单地假设内联具有全局可见性的函数是安全的.
如果你没有编译就可以做出同样的假设-fPIC,即编译器可以安全地假设可执行文件中定义的全局符号不能插入,因为查找范围以可执行文件本身开头,然后是所有其他库,包括预加载的库那些.
有关更透彻的了解,请查看以下文章.
正如其他人已经指出的那样,-fPIC迫使GCC禁用许多优化,例如内联和克隆。我想指出几种克服此问题的方法:
-fPIC,-fPIE则替换为;由于安全原因,这是现代发行版的默认设置-fvisibility=hidden并__attribute__((visibility("default")))仅从库中导出必要的功能,并隐藏其余功能;这将使GCC可以优化隐藏功能__attribute__((alias ("__f")));)从库中引用库函数;这将再次解开海湾合作委员会的手-fno-semantic-interposition在最新的GCC版本中添加的标记自动执行有趣的是,Clang与GCC有所不同,因为它默认情况下允许所有优化,而不管-fPIC(可以重写-fsemantic-interposition以获得类似GCC的行为)。