优化常数双指数的幂和

Optimize sum of powers with constant double exponent

本文关键字:指数 常数双 优化      更新时间:2023-10-16

我已经实现了一个算法,在某些时候需要计算向量元素的幂之和。功率是一个正双精度,在循环期间是恒定的。我发现,这个计算目前是我的程序的瓶颈,想知道是否有一种方法来加速以下代码片段:

double SumOfPowers(std::vector<double>& aVector,double exponent)
{
    double help = 0;
    size_t sizeOfaVector = aVector.size();
    for (size_t k = 0; k < sizeOfaVector; k++)
    {
        help += std::pow(aVector[k], exponent);
    }
    return help;
}

我有一种感觉,好像可以利用指数在循环期间是常数的事实,并减少昂贵的std::pow调用。有没有人知道更好的实现方法,或者是否有现成的库函数来完成这项工作?

首先,检查循环是否矢量化。为此,使用-O3构建程序(在这里和接下来的内容中,我假设是gcc编译器;我不太了解其他编译器,但我希望它们有类似的选项)。还添加-ftree-vectorizer-verbose=2选项,以获得环路矢量化的详细输出。您可能需要使用选项来获得您想要的输出。

如果循环未向量化,则可以将其向量化。您可能需要更改循环结构(例如,首先将所有幂计算到一个单独的数组中,然后才计算总和),或者使用某种方式向编译器告知更多信息,例如restrict声明,请参阅"使用gcc 4.7的自动向量化"了解更多讨论。在最坏的情况下,我认为,你可以手工实现向量化,我记得有这样的函数,参见英特尔的参考资料或"用c++实现SSE SIMD的实用指南"。

对于Visual Studio,首先添加/Qvec-report:2选项以获得详细报告。以上所有的建议也适用,你只需要找到相应的MSVC选项。


另一种加速的方法是通过使用-ffast-math选项来牺牲精度。据我所知,标准pow函数使用一些高级逻辑来检查底数或指数是否真的接近于1,以避免那里的精度问题。如果这不是您的情况,您可能不需要这种逻辑。我想-ffast-math会删除它,尽管您可能想要检查它。

无论如何,您可以将pow替换为exp(log(...)*...)以手动避免这些检查。这不会给您带来太多的加速,但您可能会注意到一些增益。此外,如果您经常将同一向量提高到不同的指数,您可以预先计算log s。

不,常量指数不允许可行的优化,除非您的值经常重复(如果是的话:记住)。并行化是最好的选择(或者根本不使用pow)