保存计算值而不是多次重新计算的术语是什么?

时间:2015-11-08 00:48:42

标签: optimization terminology temporary-objects

如果你有这样的代码(用java编写,但适用于任何类似的语言):

public static void main(String[] args) {
    int total = 0;
    for (int i = 0; i < 50; i++)
        total += i * doStuff(i % 2); // multiplies i times doStuff(remainder of i / 2)
}

public static int doStuff(int i) {
    // Lots of complicated calculations
}

你可以看到还有改进的余地。 doStuff(i % 2)仅返回两个不同的值 - 一个用于偶数上的doStuff(0),另一个用于奇数上的doStuff(1)。因此,每次通过说doStuff(i % 2)重新计算这些值时,你会浪费大量的计算时间/功能。你可以像这样改进:

public static void main(String[] args) {
    int total = 0;
    boolean[] alreadyCalculated = new boolean[2];
    int[] results = new int[2];
    for (int i = 0; i < 50; i++) {
        if (!alreadyCalculated[i % 2]) {
            results[i % 2] = doStuff(i % 2);
            alreadyCalculated[i % 2] = true;
        }
        total += i * results[i % 2];
    }
}

现在它访问存储的值而不是每次重新计算。保持这样的数组可能看起来很愚蠢,但对于像i = 0, i < 500那样循环的情况,以及每次重新检查i % 32或类似事件,数组都是一种优雅的方法。 / p>

这种代码优化是否有一个术语?我想更多地了解它的不同形式和惯例,但我缺乏简洁的描述。

2 个答案:

答案 0 :(得分:4)

  

是否有这种代码优化的术语?

是的,有:

  

在计算中, memoization 是一种优化技术,主要用于通过存储昂贵的函数调用的结果来加速计算机程序,并在再次出现相同的输入时返回缓存的结果。

https://en.wikipedia.org/wiki/Memoization

答案 1 :(得分:1)

Common-subexpression-elimination(CSE)与此相关。这种情况是这种情况的组合,并从循环中提升循环不变计算。

我同意CBroe的说法,你可以称之为特定形式的缓存记忆,尤其是你用笨重的alreadyCalculated数组实现它的方式。您可以优化它,因为您知道哪些调用将是新值,哪些将是重复。通常情况下,为了所有调用者的利益,您可以在被调用函数内部使用静态数组实现memoization。理想情况下,您可以使用标记值来标记尚未计算结果的条目,而不是为此维护单独的数组。或者对于稀疏的输入值集,只需使用散列(而不是例如具有2 ^ 32个条目的数组)。

您也可以避开主循环中的if

public class Optim
{
  public static int doStuff(int i) { return (i+5) << 1; }
  public static void main(String[] args)
  {

    int total = 0;
    int results[] = new int[2];

    // more interesting if we pretend the loop count isn't known to be > 1, so avoiding calling doStuff(1) for n=1 is useful.
    // otherwise you'd just do int[] results = { doStuff(0), doStuff(1) };

    int n = 50;

    for (int i = 0 ; i < Math.min(n, 2) ; i++) {
        results[i] = doStuff(i);
        total += i * results[i];
    }
    for (int i = 2; i < n; i++) { // runs zero times if n < 2
        total += i * results[i % 2];
    }
    System.out.print(total);
  }
}

当然,在这种情况下,我们可以进一步优化。 sum(0..n) = n * (n+1) / 2,因此我们可以使用它来根据doStuff(0)(偶数项的总和)和doStuff(1)(奇数项的总和)得到封闭形式(非循环)解决方案)。因此,我们每次只需要两个doStuff()结果,避免任何需要记忆。