Open mraleph opened 6 years ago
Tentatively assigning to @ErikCorryGoogle because of the work previously done on hash codes.
Drive-by comment: Your two examples look quite different since I'd expect the size of the sets to be one vs 1000000.
A closure like () => foo.tearOff()
can/should create instances that are not equal to any other function instance. A tear-off creates a new instance that's equal to other tear-offs of the same method from the same object.
(I'm guessing you might be optimizing and recognizing that foo
doesn't differ even if the scope of the function expression changes between evaluations, and you've done loop-invariant code movement on it. In either case, I'd thing a more comparable example would be:
class Foo {
int tearOff() => 10;
}
void main() {
final s = new Set();
final foo = new Foo();
tearOff() => foo.tearOff();
for (var i = 0; i < 1000000; i++) s.add(tearOff);
}
... just to be certain).
@lrhn good catch. it's much slower than 2x then.
This code:
is roughly 2x slower than this code:
This might be rather suprising for Dart developers. The reason for the slowness is the fact that Closure hash-code for tear-offs is a combination of identity hashcode for the receiver and function itself and neither of those is computed efficiently (see
Closure::ComputeHash
code)DartLibraryCalls::IdentityHashCode
, even though on 64-bit platforms there is fast path for accessing identity hash code from object header itself.Function::ComputeClosureHash
which does this:The line marked with
(!!!)
is extremely expensive - it formats function signature as a string - and it does no caching - so formatting of signature is done every time you invokef.hashCode
on a new tear-off.It seems that
Function::ComputeClosureHash
can benefit from some memoization andInstance::IdentityHashCode()
can benefit from fast-pathing on 64-bit platforms.