Dart:在多次调用的方法中编写嵌套函数是否高效

Dart: Is it efficient to write nested functions in methods that are called several times

请考虑以下代码,

class A {
  foo() {
    int a = logicToGetA();

    int _bar() => someOtherLogic(a);

    // ..

    int b = _bar();

    // ..
  }
}

class B {
  int _bar(int a) => someOtherLogic(a);

  foo() {
    int a = logicToGetA();

    // ..

    int b = _bar(a);

    // ..
  }
}

main() {
  for (int i = 0; i < 1000000; i++) {
    A().foo();
  }
  for (int i = 0; i < 1000000; i++) {
    B().foo();
  }
}

解释:class A 的 bar() 嵌套在 foo() 中,但 class B 的 bar() 嵌套在 foo() 之外。在第二种情况下,bar() 也可以作为静态方法。

我的疑问:如果 foo() 被多次调用,哪种方式更有效?如果 A().foo() 被调用 1000000 次,A.foo.bar 会被重新定义那么多次吗?

视情况而定。

如果 _bar 函数可以在 foo 方法之外定义,那么我们可以假定它不引用 foo 方法的任何局部变量。 在这种情况下,编译器 可以 优化局部函数,使其与实例方法一样高效。也许是,也许不是,让我们检查一下。

参见:https://dartpad.dev/4a53a91bf4e0006e4af4c8a598b68ee6。 这是(尝试)编写的,以便编译器无法优化 _someOtherLogic 的调用。 我还尝试将调用设为静态方法(但随后必须将对象本身作为参数传递才能为 flag 访问实例 getter)。

运行 dartpad 中的这个给了我一组最终结果

A: 918460 /ms
B: 798960 /ms
S: 918380 /ms

看起来 dart2js 使用本地函数比使用实例方法 高效。 运行 VM 上的相同代码给出的基准测试结果为:

A: 625960.0 /ms
B: 723245.0 /ms
S: 625075.0 /ms

表明它的性能特点与dart2js正好相反

很有可能dart2js在静态已知的情况下内联了_bar函数。 dart2js 编译器倾向于比 VM 更积极地进行内联。

总而言之,除非函数调用大量出现在 real-world 程序的性能配置文件中,否则我不会开始担心这种差异。 如果您的程序的性能确实严重依赖于此 one 函数调用,我可能会内联该函数。如果不是,请编写更具可读性和可维护性的内容,并且在您知道它很重要之前不要开始 micro-optimizing。