C++ 简单计算的性能问题
编辑2:程序计算时间减少16%有关计算,请参见底部C++ 简单计算的性能问题,c++,optimization,C++,Optimization,编辑2:程序计算时间减少16%有关计算,请参见底部 我已经编写了一个N体模拟器,实现了Barnes-Hut算法。现在我有了一个看起来很无辜的函数,名为CheckNode。它很简单,计算时间也不长,但问题是,它被调用了数百万次,因此它占用了每帧之间的大部分计算时间 我分析了代码,这个函数占了总计算时间的84.58%,并且只有10K个粒子,当我使用10倍的粒子时,这个函数使用的百分比越来越大 下面是函数,右边的时间百分比显示为红色 现在这里有一些令人震惊的事情,比如一个简单的if语句占用了9
我已经编写了一个N体模拟器,实现了Barnes-Hut算法。现在我有了一个看起来很无辜的函数,名为
CheckNode
。它很简单,计算时间也不长,但问题是,它被调用了数百万次,因此它占用了每帧之间的大部分计算时间
我分析了代码,这个函数占了总计算时间的84.58%,并且只有10K个粒子,当我使用10倍的粒子时,这个函数使用的百分比越来越大
下面是函数,右边的时间百分比显示为红色
现在这里有一些令人震惊的事情,比如一个简单的if语句占用了
9.17%
,另一个if语句占用了超过20%的计算时间!这里有没有哪怕是最轻微的优化,都可以通过成百上千万的函数调用来实现,从而让我的程序运行得更快
编辑:
以下是CalculateForceNode
函数:
void CalculateForceNode(Body* bi, Node* bj) //bi is being attracted to bj. 15 flops of calculation
{
//vector from the body to the center of mass
double vectorx = bj->CenterOfMassx - bi->posX;
double vectory = bj->CenterOfMassy - bi->posY;
//c^2 = a^2 + b^2 + softener^2
double distSqr = vectorx * vectorx + vectory * vectory + Softener * Softener;
// ivnDistCube = 1/distSqr^(3/2)
double distSixth = distSqr * distSqr * distSqr;
double invDistCube = 1.0f / (sqrt(distSixth));
double Accel = (bj->TotalMass * invDistCube * _GRAV_CONST);
bi->AccelX += vectorx * Accel;
bi->AccelY += vectory * Accel;
}
编辑2:
优化结果
CheckNode
函数现在占用了总计算时间的82.03%
(在1分钟37秒的样本中测量),而之前它占用了84.58%
现在逻辑告诉我们剩余的
15%
计算时间与第二个程序剩余的18%
计算时间相同。因此,这些相同的周期(相同的代码)占第一个程序的15%
,占第二个程序的18%
。让完成另一个代码的时间为x
第一个程序花了1/0.15
=6.666x
,第二个程序花了1/0.18
=5.555x
。然后你可以找到5.555x
是6.666x
的分数,它的计算结果是~0.83
,因此程序计算时间减少了(1-0.83=0.16
)16%我要尝试的第一件事是在您的条件之一中反转元素,替换:
if(withSqr / distanceSqr < nodeThresholdSqr || pNode->HasChildren == false)
交换if语句,并将所有计算移到
pNode->haschilds==false
部分中:
void CheckChildren(Node* pNode, Body* pBody)
{
if (pNode->Child[0]->Bodies.size() > 0)
CheckNode(...
}
void CheckNode(Node* pNode, Body* pBody)
{
if (pNode->hasChildren != false)
{
double diffX = (pNode->CenterOfMass - pBody->posX);
double diffY = (pNode->CenterOfMass - pBody->posY);
double distanceSqr = ((diffX * diffX) + (diffY * diffY));
double widthSqr = pNode->width * pNode->width;
if (widthSqr / distanceSqr < NodeThresholdSqr)
{
CalculateForceNode(pBody, pNode);
}
else
{
CheckChildren(pNode, pBody);
}
}
else
{
CheckChildren(pNode, pBody);
}
}
void CheckChildren(节点*pNode,主体*pBody)
{
if(pNode->Child[0]->body.size()>0)
检查节点(。。。
}
void CheckNode(节点*pNode,主体*pBody)
{
if(pNode->hasChildren!=false)
{
double diffX=(pNode->CenterOfMass-pBody->posX);
double diffY=(pNode->CenterOfMass-pBody->posY);
双距离sqr=((diffX*diffX)+(diffY*diffY));
双宽度SQR=pNode->width*pNode->width;
if(宽度SQR/距离SQR
- 首先,您应该
functioninline
或直接访问body.size()
,这样就不会有函数调用的开销(将所有需要的信息推送到堆栈并弹出它需要时间)size
- 我没有看到所有的代码,但看起来您可以预先计算
。当函数中未指定widthSqr
时,可以计算它width
- 您在这里使用了大量的指针,看起来您的结构分散在内存中。这将产生大量CPU缓存未命中。请确保计算所需的所有数据都在一个长、连续且紧凑的内存区域中
- 在
中,检查是否可以预先计算CalculateForceNode
。Softener*Softener
函数非常耗时。sqrt
算法是迭代的,因此可以通过较少的迭代来牺牲精度以提高速度,或者可以使用查找表sqrt
- 您在
中进行了两次相同的计算CalculateForceNode
void CalculateForceNode(Body* bi, Node* bj) { //vector from the body to the center of mass double vectorx = bj->CenterOfMassx - bi->posX; double vectory = bj->CenterOfMassy - bi->posY; //c^2 = a^2 + b^2 + softener^2 double distSqr = vectorx * vectorx + vectory * vectory...
vectorx、vectory和distSqr
已在CheckNode
中计算为diffX、diffY和distanceSqr
。手动内联整个函数CalculateForceNode
基于所用时间的评测是不够的,您需要知道这段时间花在了什么地方-换句话说,使用更高级的评测器。
void CalculateForceNode(Body* bi, Node* bj)
{
//vector from the body to the center of mass
double vectorx = bj->CenterOfMassx - bi->posX;
double vectory = bj->CenterOfMassy - bi->posY;
//c^2 = a^2 + b^2 + softener^2
double distSqr = vectorx * vectorx + vectory * vectory...
另外,您也没有提到任何有关您正在使用的编译器或平台的信息
对于占用9%时间的if语句,我不认为它用于比较,而是用于获取数据。您有多个间接层次(使用指向另一个指针的指针访问数据,等等)。这不利于缓存和分支预测,我猜您可能会因为分支未命中预测而花费时间从内存中获取数据或进行无用的计算,而不是进行实际的比较
我注意到的另一个注意事项是:if(pNode->haschilds==false)然后,您不需要进行所有计算来找到widthSqr。我认为您应该首先重新构造逻辑来检查这一点,如果条件为false,那么您可以计算widthSqr并继续您的逻辑。由于函数被多次调用,您应该摆脱调用
CalculateForceNode(…)的开销
通过手动内联代码。执行此操作时,您会注意到需要应用的其他技巧:
void CheckNode(Node* pNode, Body* pBody)
{
double diffX = (pNode->CenterOfMass - pBody->posX);
double diffY = (pNode->CenterOfMass - pBody->posY);
double distanceSqr = ((diffX * diffX) + (diffY * diffY));
double widthSqr = pNode->width * pNode->width;
if (widthSqr / distanceSqr < NodeThresholdSqr || pNode->hasChildren == false)
{
//vector from the body to the center of mass
double vectorx = pNode->CenterOfMassx - pBody->posX;
double vectory = pNode->CenterOfMassy - pBody->posY;
//c^2 = a^2 + b^2 + softener^2
double distSqr = vectorx * vectorx + vectory * vectory + Softener * Softener;
// ivnDistCube = 1/distSqr^(3/2)
double distSixth = distSqr * distSqr * distSqr;
double invDistCube = 1.0f / (sqrt(distSixth));
double Accel = (pNode->TotalMass * invDistCube * _GRAV_CONST);
pBody->AccelX += vectorx * Accel;
pBody->AccelY += vectory * Accel;
}
else
{
CheckChildren(pNode, pBody);
}
}
希望这对您有用。在您的if语句中,首先进行HasChildren检查,避免双除法,并进行widthSqr
void CheckNode(Node* pNode, Body* pBody)
{
double diffX = (pNode->CenterOfMass - pBody->posX);
double diffY = (pNode->CenterOfMass - pBody->posY);
double distanceSqr = ((diffX * diffX) + (diffY * diffY));
double widthSqr = pNode->width * pNode->width;
if (widthSqr / distanceSqr < NodeThresholdSqr || pNode->hasChildren == false)
{
//vector from the body to the center of mass
double vectorx = pNode->CenterOfMassx - pBody->posX;
double vectory = pNode->CenterOfMassy - pBody->posY;
//c^2 = a^2 + b^2 + softener^2
double distSqr = vectorx * vectorx + vectory * vectory + Softener * Softener;
// ivnDistCube = 1/distSqr^(3/2)
double distSixth = distSqr * distSqr * distSqr;
double invDistCube = 1.0f / (sqrt(distSixth));
double Accel = (pNode->TotalMass * invDistCube * _GRAV_CONST);
pBody->AccelX += vectorx * Accel;
pBody->AccelY += vectory * Accel;
}
else
{
CheckChildren(pNode, pBody);
}
}
void CheckNode(Node* pNode, Body* pBody)
{
double diffX = (pNode->CenterOfMass - pBody->posX);
double diffY = (pNode->CenterOfMass - pBody->posY);
double distanceSqr = ((diffX * diffX) + (diffY * diffY));
double widthSqr = pNode->width * pNode->width;
double SoftnerSq = Softener * Softener; //precompute this value
if (widthSqr / distanceSqr < NodeThresholdSqr || pNode->hasChildren == false)
{
//c^2 = a^2 + b^2 + softener^2
double distSqr = distanceSqr + SoftnerSq;
// ivnDistCube = 1/distSqr^(3/2)
double distSixth = distSqr * distSqr * distSqr;
double invDistCube = 1.0f / (sqrt(distSixth));
double Accel = (pNode->TotalMass * invDistCube * _GRAV_CONST);
pBody->AccelX += diffX * Accel;
pBody->AccelY += diffY * Accel;
}
else
{
CheckChildren(pNode, pBody);
}
}