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

698 Views Asked by At

please consider the below code,

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();
  }
}

Explanation: class A has bar() nested inside foo() but class B has bar() outside of foo(). in the second case, bar() can be made as a static method as well.

My Doubt: which way is more efficient, if foo() is called multiple times? If A().foo() is called 1000000 times, will A.foo.bar is redefined that many times?

1

There are 1 best solutions below

0
On BEST ANSWER

It depends.

If the _bar function can be defined outside of the foo method, then we can presume that it doesn't reference any local variables of the foo method. In that case, the compiler can optimize the local function to be just as efficient as the instance method. Maybe it does, maybe it doesn't, so let's check.

See: https://dartpad.dev/4a53a91bf4e0006e4af4c8a598b68ee6 . This is (attempted) written so that the compiler can't optimize away the invocation of _someOtherLogic. I also tried making the invocation be to a static method (but then having to pass the object itself as argument to give access to the instance getter for flag).

Running this in dartpad gives me a final set of results of

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

It seems dart2js is more efficient with the local function than with the instance method. Running the same code on the VM gives a benchmark result of:

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

showing that it's performance characteristics are exactly the opposite of dart2js.

It's very likely that dart2js inlines the _bar function when it's statically known. The dart2js compiler tend to be more aggressive about inlining than the VM.

All in all, I wouldn't start to worry about this difference unless the function call shows up heavily in the performance profile of a real-world program. If your program's performance really depends critically on this one function call, I'd probably inline the function. If not, write whatever is more readable and maintainable, and don't start micro-optimizing until you know it matters.