C++ C++;

C++ C++;,c++,performance,optimization,C++,Performance,Optimization,我使用以下代码计算两个向量之间的距离: double dist(vector<double> & vecA, vector<double> & vecB){ double curDist = 0.0; for (size_t i = 0; i < vecA.size(); i++){ double dif = vecA[i] - vecB[i]; curDist += dif * dif; }

我使用以下代码计算两个向量之间的距离:

double dist(vector<double> & vecA, vector<double> & vecB){
    double curDist = 0.0;
    for (size_t i = 0; i < vecA.size(); i++){
        double dif = vecA[i] - vecB[i];
        curDist += dif * dif;
    }

    return curDist;
}
负责此功能中超过77%的CPU时间。我的问题是:是否有可能以某种方式优化此功能

注:

  • 为了分析我的应用程序,我使用了英特尔放大器XE
  • 减少距离计算的数量并不是一个可行的解决方案 我
您可以取消对
vecA.size()
的调用。对于循环的每次迭代,只需在循环之前调用一次即可。您还可以进行循环展开,以便在每次循环迭代中为自己提供更多的计算。您使用的是什么编译器,以及什么优化设置?编译器通常会为您执行展开操作,但您可以手动执行。

我现在可以想到两个可能的问题:

  • 这种计算是内存限制的
  • curDist
    上存在迭代到迭代的依赖关系

此计算受内存限制。

数据集大于CPU缓存。因此,在这种情况下,除非你能重新构造你的算法,否则再多的优化也无济于事


curDist

您依赖于
curDist
。这将阻止编译器进行矢量化。(另外,不要总是将探查器编号信任于行。它们可能不准确,尤其是在编译器优化之后。)

通常,编译器向量器可以将
curDist
拆分为多个部分和,并展开/向量化循环。但在严格的浮点行为下,它无法做到这一点。如果还没有,可以尝试放松浮点模式。或者你可以把总数平分,自己摊开

例如,这种优化是编译器可以对整数进行的,但不一定要对浮点进行优化:

double curDist0 = 0.0;
double curDist1 = 0.0;
double curDist2 = 0.0;
double curDist3 = 0.0;
for (size_t i = 0; i < vecA.size() - 3; i += 4){
    double dif0 = vecA[i + 0] - vecB[i + 0];
    double dif1 = vecA[i + 1] - vecB[i + 1];
    double dif2 = vecA[i + 2] - vecB[i + 2];
    double dif3 = vecA[i + 3] - vecB[i + 3];
    curDist0 += dif0 * dif0;
    curDist1 += dif1 * dif1;
    curDist2 += dif2 * dif2;
    curDist3 += dif3 * dif3;
}

//  Do some sort of cleanup in case (vecA.size() % 4 != 0)

double curDist = curDist0 + curDist1 + curDist2 + curDist3;
double curDist0=0.0;
双电流器1=0.0;
双电流器2=0.0;
双电流器3=0.0;
对于(大小i=0;i
如果可行(如果数字范围不大),您可能希望探索使用定点存储这些数字,而不是双倍存储

定点将把这些转换成整数运算,而不是双精度运算

另一件有趣的事情是,假设您的配置文件是正确的,那么查找似乎是一个重要因素(否则乘法可能比减法更昂贵)


我会尝试使用常量向量迭代器,而不是随机访问查找。它可能有两个方面的帮助:1-它是常量,2-迭代器的串行特性可以让处理器更好地进行缓存。

如果您的平台没有(或没有使用)支持浮点数学的ALU,则浮点库的速度自然较慢,并且会消耗额外的非易失性内存。我建议改为使用32位(
long
)或64位(
long
)定点算法。然后在算法结束时将最终结果转换为浮点。几年前,我在一个项目中这样做是为了提高I2T算法的性能,它工作得非常出色。

如果这一行真的占用了一半以上的CPU时间,那么它似乎与缓存相关(即对每个元素的第一次访问需要缓存命中),并且不容易通过加速代码来进行优化。您可能需要研究基于向量的指令,以减轻向量的位(a'la SSE3)@神秘性。大小()在4到100之间变化,具体取决于应用程序输入(即数据集维度)。这是STL的向量吗?如果这里的所有人都认为它是,而不是,那将是一个耻辱。@Mooing duck-它还计算两个点之间的距离的平方,表示为坐标向量。你是否一步一个循环,一次一条指令,以确保它没有调用隐藏函数或边界检查?请告诉我
vecA.size()
只需片刻。编译器可能无法确定该值在循环期间是否保持不变,因此可能无法对其进行优化。循环展开是另一个好主意。实际上,<代码> vector::大小<代码>是一个固定的时间操作,编译器应该能够内嵌它并优化它。我使用VisualStudioC++ 2010,带有默认参数。请注意,探查器的结果显示vecA.size()不会占用CPU时间的很大一部分。“常量时间”并不意味着它与可能位于寄存器中的局部变量访问速度一样快。编译器不能保证vector::size在整个循环中保持不变。但是,如果您有一个将大小放入其中的局部变量,编译器就会知道该值不能change@TJD您是对的:取消对vecA.size()的调用确实减少了总体CPU时间。现在我的下一步是解决减法线问题,这是主要的瓶颈。我看到你正在使用VisualStudioC++ 2010。那个编译器还不支持矢量化。。。使用ICC或GCC可能会获得更好的性能。可能需要检查您的循环…;)@尼姆:谢谢你抓住了这个机会回答得好!您需要i+=4,然后从展开的循环左侧开始,在最后(最多3步)添加原始循环。@MooingDuck我通常只复制原始循环。但在我几乎所有的应用程序中,我都可以强制将数据集设置为二次幂的倍数,所以我没有
double curDist0 = 0.0;
double curDist1 = 0.0;
double curDist2 = 0.0;
double curDist3 = 0.0;
for (size_t i = 0; i < vecA.size() - 3; i += 4){
    double dif0 = vecA[i + 0] - vecB[i + 0];
    double dif1 = vecA[i + 1] - vecB[i + 1];
    double dif2 = vecA[i + 2] - vecB[i + 2];
    double dif3 = vecA[i + 3] - vecB[i + 3];
    curDist0 += dif0 * dif0;
    curDist1 += dif1 * dif1;
    curDist2 += dif2 * dif2;
    curDist3 += dif3 * dif3;
}

//  Do some sort of cleanup in case (vecA.size() % 4 != 0)

double curDist = curDist0 + curDist1 + curDist2 + curDist3;