Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

Commit

Permalink
perf(bracket): Optimize calling methods on objects.
Browse files Browse the repository at this point in the history
Optimize the closures used for calling methods on objects by getting rid
of an intermediate "field access" that loads the function from the object.
This allows us to use InstanceMirror.invoke more directly.

Performance
===========
The performance implications of this change look good. On the native Dart VM,
calling methods through the reflective path becomes 4.5x faster. When compiled
to JavaScript, the difference is even bigger and it is 6.7x faster.

Dart VM:
  new: o.f() => g: 18,885,385.384 ops/sec  r: 2,252,149.057 ops/sec   h: 2,233,514.96 ops/sec   i: 32,662,458.941 ops/sec
  old: o.f() => g: 18,118,349.692 ops/sec  r:   496,119.238 ops/sec   h: 6,234,893.68 ops/sec   i: 31,472,943.499 ops/sec

Dart2js:
  new: o.f() => g:  2,618,328.337 ops/sec  r: 1,234,723.632 ops/sec   h: 1,234,745.10 ops/sec   i:  7,507,441.131 ops/sec
  old: o.f() => g:  2,427,133.253 ops/sec  r:   184,727.903 ops/sec   h:   500,226.70 ops/sec   i:  7,461,638.467 ops/sec
  • Loading branch information
Kasper Lund authored and mhevery committed Dec 14, 2013
1 parent 2d2c521 commit 525eead
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 4 deletions.
1 change: 1 addition & 0 deletions bin/parser_generator_for_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ main(arguments) {
"a.b.c",
"x.b.c",
"e1.b",
"o.f()",
"1", "-1", "+1",
"!true",
"3*4/2%5", "3+6-2",
Expand Down
18 changes: 14 additions & 4 deletions lib/core/parser/backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class BoundExpression {
var _context;
Expression expression;

BoundExpression(this._context, this.expression);
BoundExpression(this._context, Expression this.expression);

call([locals]) => expression.eval(_context, locals);
assign(value, [locals]) => expression.assign(_context, value, locals);
Expand All @@ -17,7 +17,15 @@ class Expression implements ParserAST {
String exp;
List parts;

Expression(this.eval, [this.assign]);
// Expressions that represent field accesses have a couple of
// extra fields. We use that to generate an optimized closure
// for calling fields of objects without having to load the
// field separately.
Expression fieldHolder;
String fieldName;
bool get isFieldAccess => fieldHolder != null;

Expression(ParsedGetter this.eval, [ParsedSetter this.assign]);

bind(context) => new BoundExpression(context, this);

Expand Down Expand Up @@ -93,7 +101,7 @@ class ParserBackend {
GetterSetter _getterSetter;
FilterMap _filters;

ParserBackend(this._getterSetter, this._filters);
ParserBackend(GetterSetter this._getterSetter, FilterMap this._filters);

static Expression ZERO = new Expression((_, [_x]) => 0);

Expand Down Expand Up @@ -314,7 +322,9 @@ class FilterExpression extends Expression {
final Expression leftHandSide;
final List<Expression> parameters;

FilterExpression(this.filterFn, this.leftHandSide, this.parameters): super(null);
FilterExpression(Function this.filterFn,
Expression this.leftHandSide,
List<Expression> this.parameters): super(null);

get eval => _eval;

Expand Down
6 changes: 6 additions & 0 deletions perf/parser_perf.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ main() {

scope['a'] = new ATest();
scope['e1'] = new EqualsThrows();
scope['o'] = new OTest();

compare(expr, idealFn) {
var nf = new NumberFormat.decimalPattern();
Expand All @@ -63,6 +64,7 @@ main() {

compare('a.b.c', (scope) => scope['a'].b.c);
compare('e1.b', (scope) => scope['e1'].b);
compare('o.f()', (scope) => scope['o'].f());
compare('null', (scope) => null);
compare('x.b.c', (s, [l]) {
if (l != null && l.containsKey('x')) s = l['x'];
Expand All @@ -83,6 +85,10 @@ class BTest {
var c = 6;
}

class OTest {
f() => 42;
}

class EqualsThrows {
var b = 3;
operator ==(x) {
Expand Down

2 comments on commit 525eead

@kasperl
Copy link
Contributor

@kasperl kasperl commented on 525eead Jan 2, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a partial pull to me? The changes to ParserBackend.functionCall and ParserBackend.fieldAccess are missing so this basically doesn't optimize anything. What am I missing?

@mhevery
Copy link
Contributor

@mhevery mhevery commented on 525eead Jan 3, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea what happened here. I have remerged the code here: 12f5f67

Please sign in to comment.