Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Profiling emberjs apps

Profiling emberjs apps

27d14fa62cd583c0dd02bc0ce0b03226?s=128

Selva Ganesh

August 07, 2018
Tweet

Transcript

  1. Profiling ember apps Selva @selvagsz

  2. Performance is a inherent feature Failing to deliver; users are

    gonna churn
  3. Load performance 1. TTFP (time taken for first paint; increases

    perceived performance) 2. TTFI (time taken for first meaningful interaction) Runtime performance 1. Rendering (jank-free 60 fps) 2. Memory monitoring
  4. Memory Management JS engine does automatic memory allocation. So, we

    don’t have any role to play So, we don’t have any role to play
  5. Garbage Collection

  6. Memory leaks

  7. Where to start ? Heap snapshot Task Manager Memory Allocation

    timeline
  8. Everything on memory are not leaks. In ember world, ideally

    you’d want to look for `destroyed` objects being held Few profiling tips Force garbage collection before taking snapshots Always use Incognito/Guest window Identify & measure the leaks before fixing it
  9. One quick demo

  10. • Prototypal state leakage • Event listeners leakage • Third

    party libs • window.set*/Ember.run* timers • Module scope leakage • Global leakage In ember.js apps
  11. export default Component.extend({ init() { this._super(...arguments); this.set('tags', []); }, actions:

    { addTag(tagName) { this.tags.pushObject(tagName); }, }, }); export default Component.extend({ tags: [], actions: { addTag(tagName) { this.tags.pushObject(tagName); }, }, });
  12. export default Component.extend({ didInsertElement() { this._super(...arguments); this._closeModal = this.closeModal.bind(this); window.addEventListener('keydown',

    this._closeModal); }, willDestroyElement() { this._super(...arguments); window.removeEventListener('keydown', this._closeModal); }, closeModal(event) { if (event.which === 27) { this.onClose(); } } }); export default Component.extend({ didInsertElement() { this._super(...arguments); window.addEventListener('keydown', this.closeModal.bind(this)); }, willDestroyElement() { this._super(...arguments); window.removeEventListener('keydown', this.closeModal); }, closeModal(event) { if (event.which === 27) { this.onClose(); } } });
  13. export default TextField.extend({ didInsertElement() { this._super(...arguments); this.$().selectize({ delimiter: ',', create:

    function(input) { return { value: input, text: input }; } }); }, }); export default TextField.extend({ didInsertElement() { this._super(...arguments); this.$().selectize({ delimiter: ',', create: function(input) { return { value: input, text: input }; } }); }, willDestroyElement() { this._super(...arguments); this.$()[0].selectize.destroy(); } });
  14. export default Component.extend({ didInsertElement() { this._super(...arguments); this.setTimeoutId = setTimeout(() =>

    { // this.doSomeStuffs() }, 1000); this.runTimerId = Ember.run.later(() => { // this.doSomeStuffs() }, 1000); }, willDestroyElement() { this._super(...arguments); clearTimeout(this.setTimeoutId); Ember.run.cancel(this.runTimerId); } }); export default Component.extend({ didInsertElement() { this._super(...arguments); setTimeout(() => { // this.doSomeStuffs() }, 1000); Ember.run.later(() => { // this.doSomeStuffs() }, 1000); }, });
  15. const UNIQUE_OPTIONS_COUNTER = new Map() export default Component.extend({ options: UNIQUE_OPTIONS_COUNTER,

    actions: { addFooToOptions() { this.options.set(this, 'foo') } } }); export default Component.extend({ init() { this._super(...arguments); this.set('options', new Map()); }, actions: { addFooToOptions() { this.options.set(this, 'foo') } } });
  16. Summarising - Clear all timers/event listeners/third-party libs - Don’t pass

    the entire controller/component references around (yield the component’s public api using hash helper) - Do not set non-primitives on prototypes - Don’t increase the scope of your objects
  17. Thanks for listening :)