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

Life on the Grid

Matt
March 16, 2014

Life on the Grid

Recording performance, code practices and addressing the unique concerns of image heavy user interfaces like the Netflix clients.

Matt

March 16, 2014
Tweet

More Decks by Matt

Other Decks in Technology

Transcript

  1. Life on the Grid
    Addressing scale & performance of
    a large JavaScript application
    !
    @innerhtml / mseeley@netflix.com
    March 15 & 16, 2014
    JQUERY.TO();

    View Slide

  2. 1999

    View Slide

  3. 2006

    View Slide

  4. 2008 - 2009

    View Slide

  5. 2010

    View Slide

  6. 2011 - 2012

    View Slide

  7. 2013+

    View Slide

  8. Android tablets, Apple iPad 2+, Blu-ray players,
    Televisions, Virgin Media TiVo boxes
    Connected devices
    Media streamers
    Nintendo Wii, Nintendo Wii U, PlayStation 3, PlayStation
    4, PlayStation Vita, Xbox 360
    Game Consoles
    Apple TV, Google Chromecast, Roku 3, etc…

    View Slide

  9. View Slide

  10. View Slide

  11. HD
    Full HD
    Ultra HD
    Ultra HD has 9x more pixels than HD

    View Slide

  12. HD
    Full HD
    Ultra HD
    Ultra HD has 9x more elephants than HD

    View Slide

  13. • avoiding surprise
    • adapting for existing solutions
    • understanding the big picture
    Surviving a life on the grid is:

    View Slide

  14. Anticipate

    View Slide

  15. Hello existing solution

    View Slide

  16. Hello existing solution

    View Slide

  17. console.timeStamp('Hello world');
    !
    // ...
    "children": [{
    "startTime": 13939
    "type": "TimeStamp
    "data": {
    "message": "He
    },
    "frameId": "11284.
    "usedHeapSize": 17
    "counters": {
    "documents": 1
    "nodes": 4,
    "jsEventListen
    }
    }]
    // ...

    View Slide

  18. amp('Hello world');
    !
    // ...
    "children": [{
    "startTime": 1393965312322.3394,
    "type": "TimeStamp",
    "data": {
    "message": "Hello world"
    },
    "frameId": "11284.1",
    "usedHeapSize": 1758128,
    "counters": {
    "documents": 1,
    "nodes": 4,
    "jsEventListeners": 0
    }
    }]
    // ...
    {
    "type": "TimeSta
    "startTime": 139
    "endTime": 13939
    "data": {
    "message": "
    }
    }

    View Slide

  19. 93965312322.3394,
    amp",
    "Hello world"
    84.1",
    1758128,
    : 1,
    teners": 0
    {
    "type": "TimeStamp",
    "startTime": 1393965312322.3394,
    "endTime": 1393965312322.3394,
    "data": {
    "message": "Hello world"
    }
    }

    View Slide

  20. var timeline = new Timeline(),
    startup = timeline.mark('Startup');
    !
    // Mark startup began.
    startup.begin();
    !
    // ... heigh-ho, heigh-ho. Mark more Tasks.
    !
    // Mark startup ended. Record results.
    startup.end();
    $.post(‘https://...', timeline.data).
    fail(function () {
    // Uh-oh!
    });
    Source: https://gist.github.com/mseeley/9570215

    View Slide

  21. 0 1.00s 2.00s 3.00s
    RECORDS
    Startup
    Task A
    Task B
    Task C
    Task D
    Detailed session-level data

    View Slide

  22. 0 1.00s 2.00s 3.00s
    Task A
    Task B
    Task C
    Task D
    Aggregate task comparisons

    View Slide

  23. Speak for the Code

    View Slide

  24. Async? Sync? Both!
    var cache = {},
    getValue = function (url, callback) {
    if (url in cache) {
    callback(cache[url]);
    } else {
    $.get(url, function(data) {
    cache[url] = data;
    callback(data);
    });
    }
    };

    View Slide

  25. Async once, async always!
    var cache = {},
    getValue = function (url, callback) {
    var complete = function (data) {
    cache[url] = data;
    callback(data);
    };
    !
    if (url in cache) {
    setTimeout(complete, 0, cache[url]);
    } else {
    $.get(url, complete);
    }
    };

    View Slide

  26. myView.show();

    View Slide

  27. myView.show(true);

    View Slide

  28. myView.show(true, 50, ’linear’, 100);
    actually show?
    delay? ?
    slide in?

    View Slide

  29. myView.show(true, 50, ’linear’, 100);
    actually show?
    delay? ?
    slide in?

    View Slide

  30. myView.show(true, 50, ’linear’, 100);
    actually show?
    delay? ?
    slide in?

    View Slide

  31. myView.show(true, 50, ’linear’, 100);
    actually show?
    delay? ?
    slide in?

    View Slide

  32. myView.show(true, 50, ’linear’, 100);
    actually show?
    delay? ?
    slide in?

    View Slide

  33. myView.show({
    duration: 50,
    ease: 'linear',
    onDone: function () {
    // animation complete!
    }
    });
    Build forward compatible interfaces

    View Slide

  34. // FIXME: This stinks.
    !
    // TODO: Make this better.

    View Slide

  35. // FIXME: whaa-whaa-whaa
    !
    // TODO: whaa-whaa-whaa

    View Slide

  36. // FIXME comments should provide
    // context and be actionable.
    // Otherwise they’re code smell.

    View Slide

  37. Take only essentials

    View Slide

  38. • is more than log statements
    • changes default application behavior
    • may adversely affect performance
    • will adversely affect file-size
    Debug code…

    View Slide

  39. if (location.hostname === 'production.com') {
    // change behavior
    }

    View Slide

  40. var log = (location.hostname === 'production.com') ?
    function () {} :
    function () {
    console.log.apply(console.log, arguments);
    }
    };

    View Slide

  41. (function(global){
    var prod = location.hostname === 'production.com',
    api = ["log","debug","info","warn","error","assert","dir","dirxml",
    "trace","group","groupCollapsed","groupEnd","time","timeEnd",
    "profile","profileEnd","count","exception","table"],
    log, i, len
    ;
    !
    if (typeof global.console == "undefined" || !global.console) {
    try { global.console = {}; } catch (err) { }
    }
    !
    log = (!prod && typeof global.console.log != "undefined") ?
    global.console.log :
    function(){}
    ;
    !
    for (i=0, len=api.length; i

    View Slide

  42. sed 's/console.\(log\|debug\|info\|warn\|error\|
    assert\|dir\|dirxml\|trace\|group\|groupEnd\|time
    \|timeEnd\|profile\|profileEnd\|count\)(.*);\?//g'

    View Slide

  43. (function(global){
    var prod = location.hostname === 'production.com',
    api = ["log","debug","info","warn","error","assert","dir","dirxml",
    "trace","group","groupCollapsed","groupEnd","time","timeEnd",
    "profile","profileEnd","count","exception","table"],
    log, i, len
    ;
    !
    if (typeof global.console == "undefined" || !global.console) {
    try { global.console = {}; } catch (err) { }
    }
    !
    log = (!prod && typeof global.console.log != "undefined") ?
    global.console.log :
    function(){}
    ;
    !
    for (i=0, len=api.length; ised 's/console.\(log\|debug\|info\|warn\|error\|
    assert\|dir\|dirxml\|trace\|group\|groupEnd\|time
    \|timeEnd\|profile\|profileEnd\|count\)(.*);\?//g'
    var log = (location.hostname === 'production.com') ?
    function () {} :
    function () {
    console.log.apply(console.log, arguments);
    }
    };
    if (location.hostname === 'production.com') {
    // change behavior
    }

    View Slide

  44. var maxLists = model.maxLists;
    !
    // #ifdef DEBUG
    if (URL_PARAMS.maxLists) {
    maxLists = URL_PARAMS.maxLists;
    console.log('Override maxLists’);
    }
    // #endif

    View Slide

  45. var groceries = {
    squash: 2,
    apples: 8
    // #ifdef RUGBYSEVENS
    ,hardCider: 12
    ,munchies: 2
    // #endif
    };

    View Slide

  46. 185KB
    (that’s more than jQuery 1.110 and jQuery 2.1.0 combined)
    Removing debug code saved:
    See: https://www.npmjs.org/package/preprocessor

    View Slide

  47. Slay dragons

    View Slide

  48. Meet our dragon:
    displaying lists of lists of video stuff

    View Slide

  49. Initialize everything.
    How does Webkit do?

    View Slide

  50. Tiles intersecting
    the viewport
    Pre-painted to
    cover scrolling
    TiledBackingStore
    See:
    https://trac.webkit.org/browser/trunk/Source/WebCore/
    platform/graphics/TiledBackingStore.cpp

    View Slide

  51. The flashes of gray make
    more sense now, right?

    View Slide

  52. The do it our
    !@ selves approach.

    View Slide

  53. Populated elements
    intersecting the viewport
    Elements reduced to
    placeholders
    Postponed activity
    See:
    https://www.w3.org/Bugs/Public/show_bug.cgi?id=20246
    https://www.w3.org/Bugs/Public/show_bug.cgi?id=17842

    View Slide

  54. Seek unusual solutions

    View Slide

  55. What’s the best way to preload
    a bunch of movie posters
    without introducing jank?

    View Slide

  56. Loading Scripting Painting

    View Slide

  57. var xhr = new XMLHttpRequest(),
    remaining = imgs.length,
    onload = function () {
    --remaining;
    if (remaining === 0) {
    // done!
    } else if (remaining > 0) {
    work();
    }
    },
    work = function () {
    xhr.open('GET', imgs.pop(), true);
    xhr.send();
    };
    !
    xhr.onload = onload;
    xhr.responseType = 'blob';
    !
    work();

    View Slide

  58. <br/>// JavaScript from XHR example and an onmessage handler<br/>
    <br/>var url = URL.createObjectURL(<br/>new Blob(<br/>[document.getElementById('imgloader').textContent],<br/>{ type: 'text/javascript' }<br/>)<br/>),<br/>worker = new Worker(url);<br/>!<br/>worker.onmessage = function () {<br/>URL.revokeObjectURL(url);<br/>// done!<br/>};<br/>!<br/>worker.postMessage(imgs);<br/>

    View Slide

  59. View Slide

  60. Tight 60fps frames!
    Too much scripting & gaps
    Too much scripting & painting & gaps

    View Slide

  61. JavaScript
    IS a profession
    @innerhtml / mseeley@netflix.com
    Thank You!
    Further reading:
    http://techblog.netflix.com/2013/12/pioneering-application-design-on-tvs-tv.html
    http://techblog.netflix.com/2012/01/webkit-in-your-living-room.html
    http://jobs.netflix.com/

    View Slide

  62. JavaScript
    IS
    @innerhtml / mseeley@netflix.com
    Thank You!
    Further reading:
    http://techblog.netflix.com/2013/12/pioneering-application-design-on-tvs-tv.html
    http://techblog.netflix.com/2012/01/webkit-in-your-living-room.html
    http://jobs.netflix.com/

    View Slide