C++ 如果类声明中未定义函数,则会导致性能损失
我有一个高性能代码,其中包括在本文底部找到的类。我的问题是,当我没有在类声明中定义ADVECU函数时,而是将声明和实现分开(我更喜欢),在英特尔C++和CLAN编译器中,我损失了相当多的性能。然而,我不明白为什么。当我删除模板时,在所有编译器上,这两种方法的性能都是相同的C++ 如果类声明中未定义函数,则会导致性能损失,c++,templates,optimization,hpc,C++,Templates,Optimization,Hpc,我有一个高性能代码,其中包括在本文底部找到的类。我的问题是,当我没有在类声明中定义ADVECU函数时,而是将声明和实现分开(我更喜欢),在英特尔C++和CLAN编译器中,我损失了相当多的性能。然而,我不明白为什么。当我删除模板时,在所有编译器上,这两种方法的性能都是相同的 template<bool dim3> struct Advec4Kernel { static void advecu(double * restrict ut, double * restrict u, d
template<bool dim3>
struct Advec4Kernel
{
static void advecu(double * restrict ut, double * restrict u, double * restrict v, double * restrict w, double * restrict dzi4, const Grid &grid)
{
int ijk,kstart,kend;
int ii1,ii2,ii3,jj1,jj2,jj3,kk1,kk2,kk3;
double dxi,dyi;
ii1 = 1;
ii2 = 2;
ii3 = 3;
jj1 = 1*grid.icells;
jj2 = 2*grid.icells;
jj3 = 3*grid.icells;
kk1 = 1*grid.ijcells;
kk2 = 2*grid.ijcells;
kk3 = 3*grid.ijcells;
kstart = grid.kstart;
kend = grid.kend;
dxi = 1./grid.dx;
dyi = 1./grid.dy;
for(int k=grid.kstart; k<grid.kend; k++)
for(int j=grid.jstart; j<grid.jend; j++)
for(int i=grid.istart; i<grid.iend; i++)
{
ijk = i + j*jj1 + k*kk1;
ut[ijk] -= ( cg0*((ci0*u[ijk-ii3] + ci1*u[ijk-ii2] + ci2*u[ijk-ii1] + ci3*u[ijk ]) * (ci0*u[ijk-ii3] + ci1*u[ijk-ii2] + ci2*u[ijk-ii1] + ci3*u[ijk ]))
+ cg1*((ci0*u[ijk-ii2] + ci1*u[ijk-ii1] + ci2*u[ijk ] + ci3*u[ijk+ii1]) * (ci0*u[ijk-ii2] + ci1*u[ijk-ii1] + ci2*u[ijk ] + ci3*u[ijk+ii1]))
+ cg2*((ci0*u[ijk-ii1] + ci1*u[ijk ] + ci2*u[ijk+ii1] + ci3*u[ijk+ii2]) * (ci0*u[ijk-ii1] + ci1*u[ijk ] + ci2*u[ijk+ii1] + ci3*u[ijk+ii2]))
+ cg3*((ci0*u[ijk ] + ci1*u[ijk+ii1] + ci2*u[ijk+ii2] + ci3*u[ijk+ii3]) * (ci0*u[ijk ] + ci1*u[ijk+ii1] + ci2*u[ijk+ii2] + ci3*u[ijk+ii3])) ) * cgi*dxi;
if(dim3)
{
ut[ijk] -= ( cg0*((ci0*v[ijk-ii2-jj1] + ci1*v[ijk-ii1-jj1] + ci2*v[ijk-jj1] + ci3*v[ijk+ii1-jj1]) * (ci0*u[ijk-jj3] + ci1*u[ijk-jj2] + ci2*u[ijk-jj1] + ci3*u[ijk ]))
+ cg1*((ci0*v[ijk-ii2 ] + ci1*v[ijk-ii1 ] + ci2*v[ijk ] + ci3*v[ijk+ii1 ]) * (ci0*u[ijk-jj2] + ci1*u[ijk-jj1] + ci2*u[ijk ] + ci3*u[ijk+jj1]))
+ cg2*((ci0*v[ijk-ii2+jj1] + ci1*v[ijk-ii1+jj1] + ci2*v[ijk+jj1] + ci3*v[ijk+ii1+jj1]) * (ci0*u[ijk-jj1] + ci1*u[ijk ] + ci2*u[ijk+jj1] + ci3*u[ijk+jj2]))
+ cg3*((ci0*v[ijk-ii2+jj2] + ci1*v[ijk-ii1+jj2] + ci2*v[ijk+jj2] + ci3*v[ijk+ii1+jj2]) * (ci0*u[ijk ] + ci1*u[ijk+jj1] + ci2*u[ijk+jj2] + ci3*u[ijk+jj3])) ) * cgi*dyi;
}
ut[ijk] -= ( cg0*((ci0*w[ijk-ii2-kk1] + ci1*w[ijk-ii1-kk1] + ci2*w[ijk-kk1] + ci3*w[ijk+ii1-kk1]) * (ci0*u[ijk-kk3] + ci1*u[ijk-kk2] + ci2*u[ijk-kk1] + ci3*u[ijk ]))
+ cg1*((ci0*w[ijk-ii2 ] + ci1*w[ijk-ii1 ] + ci2*w[ijk ] + ci3*w[ijk+ii1 ]) * (ci0*u[ijk-kk2] + ci1*u[ijk-kk1] + ci2*u[ijk ] + ci3*u[ijk+kk1]))
+ cg2*((ci0*w[ijk-ii2+kk1] + ci1*w[ijk-ii1+kk1] + ci2*w[ijk+kk1] + ci3*w[ijk+ii1+kk1]) * (ci0*u[ijk-kk1] + ci1*u[ijk ] + ci2*u[ijk+kk1] + ci3*u[ijk+kk2]))
+ cg3*((ci0*w[ijk-ii2+kk2] + ci1*w[ijk-ii1+kk2] + ci2*w[ijk+kk2] + ci3*w[ijk+ii1+kk2]) * (ci0*u[ijk ] + ci1*u[ijk+kk1] + ci2*u[ijk+kk2] + ci3*u[ijk+kk3])) )
* dzi4[k];
}
}
};
模板
结构Advec4Kernel
{
静态无效advecu(双*限制ut、双*限制u、双*限制v、双*限制w、双*限制dzi4、常数网格和网格)
{
int ijk、kstart、kend;
国际ii1,ii2,ii3,jj1,jj2,jj3,kk1,kk2,kk3;
双dxi,dyi;
ii1=1;
ii2=2;
ii3=3;
jj1=1*grid.icells;
jj2=2*grid.icells;
jj3=3*grid.icells;
kk1=1*grid.ijcells;
kk2=2*grid.ijcells;
kk3=3*grid.ijcells;
kstart=grid.kstart;
kend=grid.kend;
dxi=1./grid.dx;
dyi=1./grid.dy;
对于(int k=grid.kstart;k在大多数情况下,类声明中定义的函数是内联的,这就是性能降低的原因。显然,编译器使用关键字执行一些优化。为了从这些优化中受益,函数声明必须包含restrict
关键字。Th是的,我不知道这是编译器的缺陷还是法律
代码:
//头
模板
结构Advec4Kernel
{
静态无效advecu(双*限制,双*限制,双*限制,双*限制,双*限制,常数网格&);
}
听起来像是内联失败。您看过生成的代码了吗?当您分离它时,您是否将它标记为内联
?在使用函数之前,使用函数的代码是否可以使用定义?请显示如何分离它们。dim3
在这种情况下仍然是模板参数吗?我猜您的意思是定义的,而不是声明的…如果一个成员函数没有在类定义中声明,它也不能在其他任何地方声明。@Chiel可能也在声明中声明了restrict
,就像在定义中一样?你确定这就是原因吗?它是一个如此巨大的函数。@NeilKirk这也是我所想的。这个问题一直困扰着我现在是时候了,我不想把实现放在类声明中。我可以删除模板,但这会导致大量代码重复。@Chiel当涉及模板时,我只是把它放在类定义中,因为这样比较容易。在这种情况下,bool标志不能仅仅是类成员吗?@Neil不,我不能不允许使用模板,因为内部循环中的if语句会完全破坏性能。@Chiel如果您想避免将函数模板体放在头中,并且模板参数的域受到限制(例如,只有2个值带有bool
),您可以将定义保留在源文件中,并显式实例化它们。它不可能是真正的“法律”因为C++中的代码<限制>代码是编译器的扩展,不是标准的一部分。@我同意这个观点,但是,这解决了我在CLAN和英特尔C++编译器上的问题。我还是想知道。explanation@Chiel然后您必须查阅这些编译器的文档,特别是它们如何处理restrict
扩展名。
// header
template<bool dim3>
struct Advec4Kernel
{
static void advecu(double *, double *, double *, double *, double *, const Grid &);
}
// source
template<bool dim3>
void Advec4Kernel<dim3>::advecu(double * restrict ut, double * restrict u, double * restrict v, double * restrict w, double * restrict dzi4, const Grid &grid)
{
//...
}
// header
template<bool dim3>
struct Advec4Kernel
{
static void advecu(double *restrict, double *restrict, double *restrict, double *restrict, double *restrict, const Grid &);
}