【问题标题】:when is better to save the result of a function instead of calling it again?什么时候保存函数的结果而不是再次调用它更好?
【发布时间】:2014-02-07 23:36:16
【问题描述】:

假设我在java中有这个函数

    public static Character firstNonrepeatedChar(String in) {
            int[] repeated = new int[256];

            for(int i=0; i<256; i++){
                repeated[i] = 0;
            }

            // First time calling in.length()
            for(int j=0; j<in.length(); j++){
                repeated[in.charAt(j)]++;
            }

           // Second time calling in.length()
           // I could have used "int length = in.length() and use this variable in this second loop"
            for(int j=0; j<in.length(); j++){
                if(repeated[in.charAt(j)] == 1)
                    return in.charAt(j);
            }
            return null;
        }

如您所见,我使用了 in.length() 两次。另一种方法可能是将 in.length() 保存在变量中并使用该变量。有人能告诉我这有多大的不同吗?我知道如果我想将该值使用 100 次,我应该将该值保存在一个变量中,但在这种情况下,我们要决定是再调用一个函数还是使用一个整数变量。

【问题讨论】:

  • length() 方法只返回一个字段的值。所以它是O(1)。我看到的唯一用例是可维护性。如果您想为字符串的一半运行循环,您只需更改用于存储长度的变量的值,而不是在您编写的所有 for 循环中进行这些更改。
  • 我相信在您的情况下,时间成本几乎相同。在这种情况下,我会选择可读性,即使用两次方法调用。我认为当方法调用趋于缓慢时存储值是有意义的,并且我们保证保留的值不可能在两者之间发生变化,并且有理由怀疑 Java 优化器不会自行处理它。
  • ConcurrentLinkedQueue.size() 是一个例子,你应该在循环调用它之前三思而后行,因为这是 O(N)
  • +1 你偷了我的问题 :) (不是关于 length() 而是一般来说:为什么人们通常把它放在 for() 而不是保存和使用这个值?)

标签: java optimization memory-management


【解决方案1】:

JIT 将内联简单的方法,例如 length() 如果您想提高性能,您将不得不查看不同的算法。

你可以做的就是假设一个数组已经满了 0,所以你不需要将它归零。注意:您的字符数可能 > 255。

另外我会返回一个char,因为你不能有一个null 值。

【讨论】:

  • 这种内联是否可以避免重复执行?它如何预测值没有变化以及谁来检查正确性?
  • @Leeor 在这种情况下,length() 返回一个final 字段。
  • @SillyFreak JIT 可以根据实际使用的类动态优化。如果该类是子类,它仍然可以内联该方法。
  • 是的(我猜?),但这种方式更容易,因为 JIT 甚至不必在分支到内联代码之前检查运行时类,对吧?
  • 如果没有重复字符,我必须返回 null 或抛出某种异常。我认为返回 null 是一个不错的选择,我必须使用 Character 而不是 char 作为函数返回类型!
【解决方案2】:

一般而言,当您可以衡量优化 1) 是有益的并且 2) 值得时进行优化。值得,我的意思是可读性的降低保证了性能的提高。第 1 点意味着您的更改可能是有害的,或者可能没有任何作用。

例如,添加一个局部变量会使你的方法的堆栈变大,这可能比极端环境下的运行时增益更差。此外,String.length() 只是返回一个变量的值,因此不调用该方法并不能为您节省太多。无论如何,您的 JIT 可能(并且可能会)优化循环条件,这意味着您的优化实际上并没有什么好处。

【讨论】:

  • 现代语言的一般规则是避免过早优化。
  • 请注意,“衡量优化”应该在实际代码的上下文中,而不是微基准。如果要优化的代码在应用程序中的一部分太小而无法做出可衡量的更改,请不要优化它。
  • @PM 77-1 为什么人们坚持称这种过早的优化?如果一个人写了一个函数并且可以用两种等效的方式做某事,为什么不选择一种可以节省哪怕是微小的纳秒时间的方式呢? (当然,提供两种方式都是正确的)
【解决方案3】:

这真的取决于你调用的函数。由于您正在调用String.length(),因此在同一上下文中多次调用它是完全可以的,即使返回值预计不会改变。

但是,将函数返回值缓存在变量中被认为是最佳实践,尤其是对于复杂函数。在你的情况下,差别不大。

【讨论】:

    猜你喜欢
    • 2012-06-15
    • 2011-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-08
    • 2012-01-15
    • 2010-12-28
    相关资源
    最近更新 更多