Javascript and HTML(5)
Javascript and HTML(5)
Optimisation
Optimisation
ascii.io player case study
Marcin Kulik
Marcin Kulik
Slide 2
Slide 2 text
Marcin Kulik
Marcin Kulik
Slide 3
Slide 3 text
I work at
I work at
Slide 4
Slide 4 text
I share at github & twitter as
I share at github & twitter as sickill
sickill
Slide 5
Slide 5 text
You may know me from:
You may know me from:
ascii.io, racksh, git-dude, vim-pasta, bitpocket, coloration,
ascii.io, racksh, git-dude, vim-pasta, bitpocket, coloration,
stderred...
stderred...
https://github.com/sickill
https://github.com/sickill
Slide 6
Slide 6 text
Developer's workflow
Developer's workflow
Slide 7
Slide 7 text
Write some code
Write some code
$ rails generate resource Logo
$ vim -O app/models/logo.rb spec/models/logo_spec.rb
Slide 8
Slide 8 text
Run tests
Run tests
$ rake
...........
Slide 9
Slide 9 text
Tests still running, I'll just look at facebook.com for a sec...
Tests still running, I'll just look at facebook.com for a sec...
Slide 10
Slide 10 text
Block facebook.com
Block facebook.com
$ echo "127.0.0.1 facebook.com" | sudo tee -a /etc/hosts
Slide 11
Slide 11 text
Push changes to a repository
Push changes to a repository
$ git add .
$ git commit -m "I must have been drunk."
$ git push
Slide 12
Slide 12 text
What do these actions have in
What do these actions have in
common?
common?
Slide 13
Slide 13 text
They all happen in a terminal
They all happen in a terminal
Slide 14
Slide 14 text
Screencasts
Screencasts
Slide 15
Slide 15 text
No content
Slide 16
Slide 16 text
Quick
Quick
history
history
lesson
lesson
Slide 17
Slide 17 text
No content
Slide 18
Slide 18 text
No content
Slide 19
Slide 19 text
VT100
VT100
Released in 1978
Foundation for terminals we use
Slide 20
Slide 20 text
ascii.io
ascii.io
Slide 21
Slide 21 text
No content
Slide 22
Slide 22 text
Example use cases
Example use cases
Slide 23
Slide 23 text
Tutorials
Tutorials
Rails blog app in 15 minutes? ;)
Vim
git
shell tricks
Auto-recording of shell sessions
Auto-recording of shell sessions
Slide 26
Slide 26 text
# end of .bashrc
if [ -z "$RECORDING" ]; then
export RECORDING=1
ASCII_IO_API_URL=http://localhost:3000 exec asciiio -y
fi
Thx @roy & @_solnic_ !
Thx @roy & @_solnic_ !
Slide 27
Slide 27 text
The player
The player
Slide 28
Slide 28 text
HTML5 based VT100/102
HTML5 based VT100/102
implementation
implementation
Multiple renderers:
and
Fast & responsive
Tested with Jasmine
Slide 29
Slide 29 text
How the player works?
How the player works?
Slide 30
Slide 30 text
View / Markup
View / Markup
...
Slide 31
Slide 31 text
No content
Slide 32
Slide 32 text
Player loop
Player loop
function scheduleFrame() {
var frame = getNextFrame(n++);
var data, delay = frame[0], frame[1];
setTimeout(function() { processFrame(data) }, delay);
}
function processFrame(data) {
var changes = interpret(data);
render(changes);
scheduleFrame();
}
scheduleFrame();
Slide 33
Slide 33 text
What do terminal apps do to a
What do terminal apps do to a
terminal?
terminal?
insert, cut and overwrite text
change fg/bg color for next printing operation
jump the cursor to any line/column and do
above things
Slide 34
Slide 34 text
Buffer data model
Buffer data model
totalLines = 4
totalColumns = 10
lines = [line0, line1, line2, line3]
lineX = [cell0, cell1, ... cell9]
cellX = [character, brush]
Slide 35
Slide 35 text
Example buffer content
Example buffer content
lines = [
[ ["f", b1], ["o", b2], ["w", b1], ["a", b1] ], # first line
[ .... ] # second line
]
cell = lines[0][1]
cell[0] # => "o"
cell[1] # => b2
Resulting DOM structure
Resulting DOM structure
fowa
...
XXXX
Slide 43
Slide 43 text
Optimisation result
Optimisation result
On average 80% less span elements for each line
On average 80% less span elements for each line
~3.5x faster line rendering
~3.5x faster line rendering
Getting line element (100,000 times)
Getting line element (100,000 times)
line = this.$('.line:eq(' + n + ')')[0] // sloppy dev version
// 16.0 sec
Slide 47
Slide 47 text
Getting line element (100,000 times)
Getting line element (100,000 times)
line = this.$('.line:eq(' + n + ')')[0] // sloppy dev version
// 16.0 sec
line = this.$('.line').eq(n)[0]
// 3.5 sec
Slide 48
Slide 48 text
Getting line element (100,000 times)
Getting line element (100,000 times)
line = this.$('.line:eq(' + n + ')')[0] // sloppy dev version
// 16.0 sec
line = this.$('.line').eq(n)[0]
// 3.5 sec
line = this.$('.line')[n]
// 2.4 sec
Slide 49
Slide 49 text
Getting line element (100,000 times)
Getting line element (100,000 times)
line = this.$('.line:eq(' + n + ')')[0] // sloppy dev version
// 16.0 sec
line = this.$('.line').eq(n)[0]
// 3.5 sec
line = this.$('.line')[n]
// 2.4 sec
this.lines = this.$('.line') // set earlier
line = this.lines[n]
// 0.15 sec
Slide 50
Slide 50 text
Updating line element (10,000 times)
Updating line element (10,000 times)
line = $('span.line:first')
line.replaceWith(
'foo * 30'
)
// 3.7 sec
Slide 51
Slide 51 text
Updating line element (10,000 times)
Updating line element (10,000 times)
line = $('span.line:first')
line.replaceWith(
'foo * 30'
)
// 3.7 sec
line[0].innerHTML =
'foo * 30'
// 8.5 sec
Slide 52
Slide 52 text
Updating line element (10,000 times)
Updating line element (10,000 times)
line = $('span.line:first')
line.replaceWith(
'foo * 30'
)
// 3.7 sec
line[0].innerHTML =
'foo * 30'
// 8.5 sec
line[0].innerHTML =
'foo * 30'
// 3.0 sec (winner!)
Slide 53
Slide 53 text
Final DOM structure
Final DOM structure
fowa
...
XXXX
Slide 54
Slide 54 text
Optimisation result
Optimisation result
renderLine before: 6.7 sec
renderLine before: 6.7 sec
renderLine after: 4.4 sec
renderLine after: 4.4 sec
~35% faster line rendering
~35% faster line rendering
Slide 55
Slide 55 text
3. Use
3. Use
Slide 56
Slide 56 text
ascii.io player supports multiple
ascii.io player supports multiple
renderers
renderers
, with "copy & paste"
2d , faster, no "copy & paste"
WebGL based is in plans
Slide 57
Slide 57 text
vs
vs
Slide 58
Slide 58 text
calling renderLine 10,000 times
calling renderLine 10,000 times
renderLine(0, [['foo', brush], ['bar', brush], ['baaaz', brush]])
: 1.4 sec
: 0.9 sec
Slide 59
Slide 59 text
Optimisation result
Optimisation result
Additional 35% faster line rendering
Additional 35% faster line rendering
* but no copy/paste functionality
* but no copy/paste functionality
4. Use rAF
4. Use rAF
requestAnimationFrame
requestAnimationFrame
"The secret to silky smooth JavaScript animation"
Slide 62
Slide 62 text
Usual animation code:
Usual animation code:
function draw() {
// Drawing code goes here
}
setInterval(draw, 100);
Or:
Or:
function draw() {
setTimeout(draw, 100);
// Drawing code goes here
}
draw();
Slide 63
Slide 63 text
The problem
The problem
You
You tell the browser when to paint
tell the browser when to paint
Slide 64
Slide 64 text
Why is this a problem?
Why is this a problem?
Browsers have rendering loop for
updating view
...that is optimized to be in sync with
screen refresh rate
...and optimized to update all changed
page elements in single batch
Slide 65
Slide 65 text
Why is this a problem?
Why is this a problem?
By telling the browser when to render
...you don't care what else is
happening in the browser
...you cause additional CPU wake-ups
...you're making browser's UI less
responsive
Slide 66
Slide 66 text
requestAnimationFrame to the rescue
requestAnimationFrame to the rescue
Native API for running any type of
animation in the browser
Proposed by Mozilla
Adopted and improved by WebKit
team
Slide 67
Slide 67 text
When to render?
When to render?
The browser just knows better.
The browser just knows better.
Slide 68
Slide 68 text
Then
Then
function draw() {
setTimeout(draw, 100);
// Drawing code goes here
}
draw();
Now
Now
function draw() {
requestAnimationFrame(draw);
// Drawing code goes here
}
draw();
Slide 69
Slide 69 text
If you have a painting loop
If you have a painting loop
Only collect the changes to be painted
Paint in requestAnimationFrame
callback
Slide 70
Slide 70 text
Then
Then
function scheduleFrame() {
...
setTimeout(function() { processFrame(data) }, delay);
}
function processFrame(data) {
var changes = interpret(data);
render(changes);
scheduleFrame();
}
scheduleFrame();
Slide 71
Slide 71 text
Now
Now
function scheduleFrame() {
...
setTimeout(function() { processFrame(data) }, delay);
}
function processFrame(data) {
var changes = interpret(data);
this.changes = mergeChanges(this.changes, changes);
scheduleFrame();
}
scheduleFrame();
Slide 72
Slide 72 text
Then
Then
function render(changes) {
for (var n in changes) {
renderLine(n, changes[n]);
}
}
Now
Now
function render() {
requestAnimationFrame(render);
for (var n in this.changes) {
renderLine(n, this.changes[n]);
}
this.changes = {};
}
render();
Slide 73
Slide 73 text
Optimisation result
Optimisation result
Smoother, jitter-free animation
Smoother, jitter-free animation
Browser UI as responsive as with no animation
Browser UI as responsive as with no animation
Slide 74
Slide 74 text
5. Use Web Workers
5. Use Web Workers
Slide 75
Slide 75 text
What are Web Workers?
What are Web Workers?
scripts that run in background thread
window and document not available
setTimeout, setInterval and XHR
available
used for computationally expensive
tasks
Slide 76
Slide 76 text
Page script:
Page script:
var worker = new Worker('my_task.js');
worker.onmessage = function(event) {
console.log("Worker said : " + event.data);
};
worker.postMessage('ali');
my_task.js
my_task.js
self.postMessage("I'm working before postMessage('ali').");
self.onmessage = function(event) {
self.postMessage('Hi '+ event.data);
};
Slide 77
Slide 77 text
Web Workers in ascii.io
Web Workers in ascii.io
Slide 78
Slide 78 text
function processFrame(data) {
var changes = interpret(data);
this.changes = mergeChanges(this.changes, changes);
scheduleFrame();
}
Slide 79
Slide 79 text
Example data stream to parse
Example data stream to parse
data = "Hello FOWA!\nSome colors: \x1b[31mred, \x1b[1mbold"
Slide 80
Slide 80 text
Escape sequence for making text red
Escape sequence for making text red
\x1b[31m