【问题标题】:Optimize sum of powers with constant double exponent优化具有恒定双指数的幂和
【发布时间】:2015-05-28 12:56:53
【问题描述】:

我已经实现了一种算法,它在某些时候需要计算向量元素的幂的总和。功率是一个正双倍,在循环期间是恒定的。我想通了,这个计算目前是我程序的瓶颈,想知道是否有办法加快以下代码 sn-p:

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 调用。有谁知道更好的实现方式,或者是否有一个现成的库函数可以完成这项工作?

【问题讨论】:

  • 我会首先检查循环是否被矢量化。
  • @Petr:我该如何检查?
  • 你的向量有多少条目?
  • @m.s.通常在 1 到 100 之间变化
  • 要么反汇编并检查生成的指令,要么使用一些编译器选项。对于 gcc,这是-ftree-vectorizer-verbose=2。当然,首先打开矢量化。对于 gcc,最简单的方法是设置-O3

标签: c++ optimization


【解决方案1】:

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

如果循环未矢量化,则可以将其矢量化。您可能需要更改循环结构(例如,首先将所有幂计算到一个单独的数组中,然后才计算总和),或者使用某种方式将更多信息告诉编译器,例如restrict 声明,请参阅@ 987654321@ 进行更多讨论。在最坏的情况下,我想,你可以手动实现向量化,我记得有这样的功能,见Intel's reference"A practical guide to SSE SIMD with C++"

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


另一种加快速度的方法是使用-ffast-math 选项来牺牲精度。 AFAIK,标准pow 函数使用一些高级逻辑来检查基数或指数是否真正接近 1 的情况,以避免出现精度问题。如果这不是你的情况,你可能不需要那个逻辑。我想-ffast-math 会删除它,尽管您可能想检查一下。

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

【讨论】:

  • 如果经常需要不同的幂,那么乘以对数的建议可能很有用 - 在这种情况下,存储对数而不是或与普通值一样好。
  • @Petr 我检查了矢量化信息输出。 (除了 /Qvec-report:2 还需要将警告至少设置为级别 4 (/W4)。)循环未矢量化。我更改了代码以使用临时向量来存储幂,然后将它们与 std::accumulate 相加。现在循环被矢量化了,我在 sizeOfaVector = 10 时减少了 22% 的时间,在 sizeOfaVector = 100 时减少了 48% 的时间。太棒了!我今天学到了很多。非常感谢。
【解决方案2】:

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

【讨论】:

  • 这个函数在一个并行循环中被调用,所以不幸的是,并行化对这个特定的问题没有帮助。此外,这些值不会经常重复。也许我不得不面对这样一个事实,我正在触及优化的极限。
猜你喜欢
  • 2015-07-08
  • 2015-04-01
  • 2013-04-08
  • 1970-01-01
  • 2014-10-11
  • 2019-03-15
  • 2014-04-22
  • 2015-11-19
  • 2015-08-15
相关资源
最近更新 更多