MVC Performance, Ember.js

Post on 17-May-2015

1,401 views 1 download

Tags:

description

About how a core runtime and overall principles design influences the frontend framework performance. Comparison examples from Ember.js and YUI3

transcript

MVC Performance Ember.js

Standa Opichal@opichals

MVC

Server Client

ViewControllerModel HTML

Single Page App MVC

Server Client

VCM HTMLVCM ?

SPA Load Performance

VCM HTML?

JS

VCM

ClientServer

Client MVC Performance

ViewControllerModel

Runtime PerformanceExpressiveness (Clarity + Brevity)Performance Conscious Concepts

Framework Performance

Instance ConstructionEventingKey-Value Observing

Runtime Performance

ViewControllerModel

JavaScript

var brendan = { name: ‘Brendan’};

Performance: very fastKVO: not really

Construction

Object

var eric = new Y.Base({ name: ‘Eric’})

var tom = Ember.Object.create({ name: ‘Tom’})

Construction: KVO

KVO

var tom = Ember.Object.create({ name: ‘Tom’

_nameDidChange: function() { }.observes(‘name’)

firstName: function() { }.property(‘name’)})

Construction: KVO

KVO

for (var i=0; i<10000; i++) {constructSingleKVO();

}

SynchronousBlocks browser event loop till finishedIs it fast enough?

Construction Performance

KVO

Expected Construction PerformanceTime

Loop

cou

nt

Expectation

KVO

Construction Performance WTFTime

Loop

cou

nt

RealitySynchronous for loop!? WTF?

GCView

ControllerModel

Lifecycle EventsInitializingDestroying

KVO Book KeepingObservers

Varies significantly with JSVM, Browser, OS

Construction Performance Summary

ViewControllerModel

Eventing

Barebone eventing in YUI vs Ember.js

Eventing: YUI

kvo.fire(‘export’, { report: item })

kvo.on(‘export’, function onExport(e) {e.report.focus(); // facadee.preventDefault(); // DOM-likee.stop();

});kvo.after(‘export’, function afterExport(e) {

e.report.loseFocus();});

Eventing: Ember.js

controller.send(‘export’, { report: item })

// route actionexport: function onExport(args) {

args.report.focus(); // facade}

Eventing Performance

Facading, DOM-like events, default actions-> Lots of throwaway instances

-> frequent GC events

Simple method calls win!

YUIkeyChange DOM-like Events

Ember.jsDeclarative Observer NotationEventing is not used

KVO

Ember.Object.create({name: ‘Tom’,content: Em.Object.create({ name: ‘Jerry’ }),

nameDidChange: function() {// this runs upon name change

}.observes(‘name’, ‘content.name’)});

Ember.js Observers

Ember.js wins

Observers do by design way lesswork to function

+ Declarative is also readable

KVO Performance

ExpressivenessTemplatesLive BindingsDependency Injection

Ember.js ExpressivenessApp.UserController = Ember.ObjectController.extend({

content: Em.Object.create({name: ‘Tom’

});firstNameBinding: ‘content.name’

});

user-template:User: {{firstName}}

Performance Conscious Design

On-DemandAsync

Ember.js wins

Observers do by design way lesswork to function

+ Declarative is also readable

Example: GoodData Dashboard

Server Data

var tab = { items: [ // items.length ~ 100

{ type: ‘filter’ },{ type: ‘report’ },{ type: ‘line’ },...

]}

Dashboard Model

Tab = Em.Object.extend({ // KVOitems: Array<TabItem>

})

TabItem = Em.Object.extend({type: ...

})

Materialization

Server Data -> Model Instances

var tab = Tab.create(tabData)var items = tab.get(‘items’);tabData.items.forEach(function(item) {

items.push(TabItem.create(item))})

Slow: Dashboard ~ 10 Tabs * 100 items

// Want no TabItem instances created at firstvar tab = Tab.create({tabData: tabData});

// On-demand TabItem creationtab.get(‘items’);

Lazy Materialization FTW

Tab = Em.Object.extend({tabData: {...}items: function() {

return this.get(‘tabData’).items.map(function(item) {return TabItem.create(item);

})}.property(‘tabData’),

})

Lazy Materialization

Tab = Em.Object.extend({tabData: {...}items: function() {

...}.property(‘tabData’),

_itemsDidChange: function() {var types = this.get(‘items’).filter(...);// does stuff every items/tabData change

}.observes(‘items’); // gets ‘items’ on Tab creation})

Does it work?

Tab = Em.Object.extend({tabData: {...}items: function() { … }.property(‘tabData’),init: function () {

set(‘tabData’, {});// not declarative, unclear why, ugly// -> sooner or later somebody// _will_ forget and refactor to declarativethis.addObserver(‘items’, this._itemsDidChange);

}});

Ugly on-demand addObserver

Tab = Em.Object.extend({items: function() { … }.property(‘tabData’),

_itemsDidChange: function() {var types = this.get(‘items’).filter(...);

}.observes(‘items’);});

Observers are initialized after the ‘items’ computed property materialization (a tab.get(‘items’) call)

Since Ember.js 1.0-rc8

Use of Object.observe()Would make observing asyncCurrent Chrome CanaryIn Ember 2.0 (?)

Performance Future

Thank You!