【问题标题】:Fastest way to iterate over n-dimensional array of arbitrary extents?迭代任意范围的n维数组的最快方法?
【发布时间】:2015-01-06 18:29:31
【问题描述】:

在 C++ 中,我希望迭代一个 n 维数组,其范围分别为 min[n] 到 max[n],并在整个过程中分别保持 ord[n] 中的纵坐标。

即。一个通用的解决方案:

for (int x = 0; x < 10; x++)
for (int y = 3; y < 20; y++)
for (int z = -2; z < 5; z++)
...
   doSomething(x, y, z ...)

形式:

int min[n] {0,  3, -2 ...}
int max[n] {10, 20, 5 ...}
int ord[n] {0,  0,  0 ...};

int maxIterations = (max[0] - min[0]) * (max[1] - min[1]) * ....
for (int iteration = 0; iteration < maxIterations; iteration++)
   doSomething(ord)
   iterate(n, ord, min, max)

我能想到的 iterate() 最快的算法是:

inline void iterate(int dimensions, int* ordinates, int* minimums, int* maximums)
{
    // iterate over dimensions in reverse...
    for (int dimension = dimensions - 1; dimension >= 0; dimension--)
    {

        if (ordinates[dimension] < maximums[dimension])
        {
            // If this dimension can handle another increment... then done.
            ordinates[dimension]++;
            break;
        }

        // Otherwise, reset this dimension and bubble up to the next dimension to take a look
        ordinates[dimension] = minimums[dimension];
    }
}

这会根据需要递增和重置每个纵坐标,避免调用堆栈或任何数学运算。

有更快的算法吗?

【问题讨论】:

  • 你测过运行时间吗? doSomething(x, y, z ...) 很快吗?您的第一个版本(3 for 循环)不应该由任何体面的编译器展开吗?
  • tgmath - 是的,编译器可能会将硬编码的循环展开为 3 级,但我需要一个 N 级循环,其中 N 在编译时未知,而且我目前正在处理 100 万次以上的迭代,这可能不展开。我对迭代算法的优化感兴趣,而不是对 doSomething() 的优化。

标签: c++ algorithm optimization multidimensional-array n-dimensional


【解决方案1】:

除非您开始执行类似于Gray codes 的操作,这将改变您的遍历顺序(并且可能会非常复杂),否则您几乎可以达到预期效果。实际上,iterate 的摊销时间已经是O(1),假设每个维度的最小值不等于最大值。

最坏的情况是所有d 维度都有maximum = minimum + 1。也就是说,任何特定维度的每隔一个增量都会溢出到下一个维度。但是请注意,特定维度x(从1d)所需的数字更改总数为2^(d + 1 - x) - 1。这显然小于2^(d + 1 - x)。在所有维度上求和(1d)是一个简单的几何总和,它产生 2^(d + 1) - 2,这显然小于 2^(d + 1)。注意迭代次数是2^d,因此每次迭代的平均时间是一个常数:2^(d + 1) / 2^d = 2

如果你真的必须加快速度,可能最好的办法就是低级调整:

  • 维数是否已知且小于一个小的(例如,20 或更少)常数?然后您可以通过展开循环来消除for 循环。如果您的编译器可以推断出dimensions 是恒定的,那么您的编译器可能已经足够聪明地执行此操作,或者您可能必须创建具有恒定尺寸或手动展开循环的多个版本的iterate。 (如果你想给它一个好的 API,你可以使用模板。)
  • 实际上,您可以在 big 外循环(其中包含 doSomething 调用的那个)中摆脱 maxIterations/iteration 检查,并允许您的迭代函数在它发生时更改布尔值用完它可以增加的尺寸。这会将您的 for 循环减少到 while (keepGoing) { ... }
  • 传递具有每个维度的最小值和最大值的结构数组可能会稍微快一些,但我希望缓存几乎可以完全减轻这些好处。

当然,在任何此类更改之前和之后进行基准测试 - 每个架构和工具链的反应都不同。

【讨论】:

  • 感谢您的反馈!
猜你喜欢
  • 1970-01-01
  • 2023-02-06
  • 2013-02-01
  • 2022-08-10
  • 2016-07-22
  • 2013-04-03
  • 1970-01-01
  • 2016-09-27
  • 1970-01-01
相关资源
最近更新 更多