SlideShare a Scribd company logo
1 of 136
Download to read offline
CSS Parsing
performance tips & tricks
Roman Dvornov
Avito
Moscow, September 2016
Frontend lead in Avito
Specializes in SPA
Maintainer of:

basis.js, CSSO, 

component-inspector, 

csstree and others
CSS parsing
(russian)
3
tinyurl.com/csstree-intro
This talk is the continuation of
CSSTree
CSSTree – 

fastest detailed CSS parser
5
How this project was born
About a year ago I started 

to maintain CSSO
(a CSS minifier)
7
github.com/css/csso
CSSO was based on Gonzales
(a CSS parser)
8
github.com/css/gonzales
What's wrong with Gonzales
• Development stopped in 2013
• Unhandy and buggy AST format
• Parsing mistakes
• Excessively complex code base
• Slow, high memory consumption, pressure for GC
9
But I didn’t want 

to spend my time developing the
parser…
10
Alternatives?
You can find a lot of CSS parsers
12
Common problems
• Not developing currently
• Outdated (don't support latest CSS features)
• Buggy
• Unhandy AST
• Slow
13
PostCSS parser is a good
choice if you need one now
14
postcss.org
PostCSS pros
• Сonstantly developing
• Parses CSS well, even non-standard syntax 

+ tolerant mode
• Saves formatting info
• Handy API to work with AST
• Fast
15
General con:
selectors and values are not parsed
(are represented as strings)
16
That forces developers to
• Use non-robust or non-effective approaches
• Invent their own parsers
• Use additional parsers:

postcss-selector-parser

postcss-value-parser
17
Switching to PostCSS meant writing 

our own selector and value parsers,
what is pretty much the same as
writing an entirely new parser
18
However, as a result of a continuous
refactoring within a few months 

the CSSO parser was completely rewrote
(which was not planned)
19
And was extracted 

to a separate project
github.com/csstree/csstree
20
Performance
CSSO – performance boost story
(russian)
22
tinyurl.com/csso-speedup
My previous talk about parser performance
After my talk on HolyJS
conference the parser's
performance was improved 

one more time :)
23
* Thanks Vyacheslav @mraleph Egorov for inspiration
24
CSSTree: 24 ms
Mensch: 31 ms
CSSOM: 36 ms
PostCSS: 38 ms
Rework: 81 ms
PostCSS Full: 100 ms
Gonzales: 175 ms
Stylecow: 176 ms
Gonzales PE: 214 ms
ParserLib: 414 ms
bootstrap.css v3.3.7 (146Kb)
github.com/postcss/benchmark
Non-detailed AST
Detailed AST
PostCSS Full =
+ postcss-selector-parser
+ postcss-value-parser
Epic fail
as I realised later I extracted 

the wrong version of the parser
25
😱
github.com/csstree/csstree/commit/57568c758195153e337f6154874c3bc42dd04450
26
CSSTree: 24 ms
Mensch: 31 ms
CSSOM: 36 ms
PostCSS: 38 ms
Rework: 81 ms
PostCSS Full: 100 ms
Gonzales: 175 ms
Stylecow: 176 ms
Gonzales PE: 214 ms
ParserLib: 414 ms
bootstrap.css v3.3.7 (146Kb)
github.com/postcss/benchmark
Time after parser
update
13 ms
Parsers: basic training
Main steps
• Tokenization
• Tree assembling
28
Tokenization
30
• whitespaces – [ nrtf]+
• keyword – [a-zA-aZ…]+
• number – [0-9]+
• string – "string" or 'string'
• comment – /* comment */
• punctuation – [;,.#{}[]()…]
Split text into tokens
31
.foo {
width: 10px;
}
[
'.', 'foo', ' ', '{',
'n ', 'width', ':',
' ', '10', 'px', ';',
'n', '}'
]
We need more info about every
token: type and location
32
It is more efficient 

to compute type and location
on tokenization step
33
.foo {
width: 10px;
}
[
{
type: 'FullStop',
value: '.',
offset: 0,
line: 1,
column: 1
},
…
]
Tree assembling
35
function getSelector() {
var selector = {
type: 'Selector',
sequence: []
};
// main loop
return selector;
}
Creating a node
36
for (;currentToken < tokenCount; currentToken++) {
switch (tokens[currentToken]) {
case TokenType.Hash: // #
selector.sequence.push(getId());
break;
case TokenType.FullStop: // .
selector.sequence.push(getClass());
break;
…
}
Main loop
37
{
"type": "StyleSheet",
"rules": [{
"type": "Atrule",
"name": "import",
"expression": {
"type": "AtruleExpression",
"sequence": [ ... ]
},
"block": null
}]
}
Result
Parser performance boost
Part 2: new horizons
39
[
{
type: 'FullStop',
value: '.',
offset: 0,
line: 1,
column: 1
},
…
]
Token's cost:
24 + 5 * 4 + array =
min 50 bytes per token
Our project ~1Mb CSS
254 062 tokens
=
min 12.7 Mb
Out of the box: changing
approach
Compute all tokens at once and then
assembly a tree is much more easy,
but needs more memory, therefore is
slower
41
Scanner
(lazy tokenizer)
42
43
scanner.token // current token or null
scanner.next() // going to next token
scanner.lookup(N) // look ahead, returns
// Nth token from current token
Key API
44
• lookup(N)

fills tokens buffer up to N tokens (if they are not
computed yet), returns N-1 token from buffer
• next()

shift token from buffer, if any, or compute 

next token
Computing the same number of tokens, 

but not simultaneously 

and requires less memory
45
Problem:
the approach puts pressure on GC
46
Reducing token's cost
step by step
48
[
{
type: 'FullStop',
value: '.',
offset: 0,
line: 1,
column: 1
},
…
]
Type as string is easy to
understand, but it's for
internal use only and we
can replace it by numbers
49
[
{
type: FULLSTOP,
value: '.',
offset: 0,
line: 1,
column: 1
},
…
]
…
// '.'.charCodeAt(0)
var FULLSTOP = 46;
…
50
[
{
type: 46,
value: '.',
offset: 0,
line: 1,
column: 1
},
…
]
51
[
{
type: 46,
value: '.',
offset: 0,
line: 1,
column: 1
},
…
]
We can avoid substring
storage in the token – it's very
expensive for punctuation
(moreover those substrings
are never used);
Many constructions are
assembled by several
substrings. One long substring
is better than 

a concat of several small ones
52
[
{
type: 46,
value: '.',
offset: 0,
line: 1,
column: 1
},
…
]
[
{
type: 46,
start: 0,
end: 1,
line: 1,
column: 1
},
…
]
53
[
{
type: 46,
start: 0,
end: 1,
line: 1,
column: 1
},
…
]
Look, Ma!
No strings just numbers!
54
Moreover not an Array, but TypedArray
Array 

of objects
Arrays

of numbers
Array vs. TypedArray
• Can't have holes
• Faster in theory (less checking)
• Can be stored outside the heap (when big
enough)
• Prefilled with zeros
55
56
[
{
type: 46,
start: 0,
end: 1,
line: 1,
column: 1
},
…
]
Uint8Array
Uint32Array
Uint32Array
Uint32Array
Uint32Array
1
4
4
4
4
17 per token
(tokens count) 254 062 x 17 = 4.3Mb
4.3Mb vs. 12.7Mb(min)
57
Houston we have a problem:
TypedArray has a fixed length,

but we don't know how many tokens
will be found
58
59
[
{
type: 46,
start: 0,
end: 1,
line: 1,
column: 1
},
…
]
Uint8Array
Uint32Array
Uint32Array
Uint32Array
Uint32Array
1
4
4
4
4
17 per token
(symbols count) 983 085 x 17 = 16.7Mb
16.7Mb vs. 12.7Mb (min)
60
16.7Mb vs. 12.7Mb (min)
60
Don't give up, 

let's look on arrays
more attentively
61
start = [ 0, 5, 6, 7, 9, 11, …, 35 ]
end = [ 5, 6, 7, 9, 11, 12, …, 36 ]
61
start = [ 0, 5, 6, 7, 9, 11, …, 35 ]
end = [ 5, 6, 7, 9, 11, 12, …, 36 ]
…
62
start = [ 0, 5, 6, 7, 9, 11, …, 35 ]
end = [ 5, 6, 7, 9, 11, 12, …, 36 ]
offset = [ 0, 5, 6, 7, 9, 11, …, 35, 36 ]
start = offset[i]
end = offset[i + 1]
+
=
63
[
{
type: 46,
start: 0,
end: 1,
line: 1,
column: 1
},
…
]
Uint8Array
Uint32Array
Uint32Array
Uint32Array
Uint32Array
1
4
4
4
4
13 per token
983 085 x 13 = 12.7Mb
64
a {
top: 0;
}
lines = [
1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3
]
columns = [
1, 2, 3, 4,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
1
]
lines & columns
64
a {
top: 0;
}
lines = [
1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3
]
columns = [
1, 2, 3, 4,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
1
]
lines & columns
65
line = lines[offset];
column = offset - lines.lastIndexOf(line - 1, offset);
lines & columns
65
line = lines[offset];
column = offset - lines.lastIndexOf(line - 1, offset);
lines & columns
It's acceptable only for short lines,
that's why we cache the last line
start offset
66
[
{
type: 46,
start: 0,
end: 1,
line: 1,
column: 1
},
…
]
Uint8Array
Uint32Array
Uint32Array
Uint32Array
Uint32Array
1
4
4
4
4
9 per token
983 085 x 9 = 8.8Mb
67
8.8Mb vs. 12.7Mb(min)
Reduce operations with strings
Performance «killers»*
• RegExp
• String concatenation
• toLowerCase/toUpperCase
• substr/substring
• …
69
* Polluted GC pulls performance down
Performance «killers»*
• RegExp
• String concatenation
• toLowerCase/toUpperCase
• substr/substring
• …
70
* Polluted GC pulls performance down
We can’t avoid using
these things, but we
can get rid of the
rest
71
var start = scanner.tokenStart;
…
scanner.next();
…
scanner.next();
…
return source.substr(start, scanner.tokenEnd);
Avoid string concatenations
72
function cmpStr(source, start, end, str) {
if (end - start !== str.length) {
return false;
}
for (var i = start; i < end; i++) {
var sourceCode = source.charCodeAt(i);
var strCode = str.charCodeAt(i - start);
if (sourceCode !== strCode) {
return false;
}
}
return true;
}
String comparison
No substring!
73
function cmpStr(source, start, end, str) {
if (end - start !== str.length) {
return false;
}
for (var i = start; i < end; i++) {
var sourceCode = source.charCodeAt(i);
var strCode = str.charCodeAt(i - start);
if (sourceCode !== strCode) {
return false;
}
}
return true;
}
String comparison
Length fast-check
74
function cmpStr(source, start, end, str) {
if (end - start !== str.length) {
return false;
}
for (var i = start; i < end; i++) {
var sourceCode = source.charCodeAt(i);
var strCode = str.charCodeAt(i - start);
if (sourceCode !== strCode) {
return false;
}
}
return true;
}
String comparison
Compare strings 

by char codes
Case insensitive comparison of
strings*?
75
* Means avoid toLowerCase/toUpperCase
Heuristics
• Comparison with the reference strings only (str)
• Reference strings may be in lower case and
contain latin letters only (no unicode)
• I read once on Twitter…
76
Setting of the 6th bit to 1 changes upper case
latin letter to lower case
(works for latin ASCII letters only)
'A' = 01000001
'a' = 01100001
'A'.charCodeAt(0) | 32 === 'a'.charCodeAt(0)
77
78
function cmpStr(source, start, end, str) {
…
for (var i = start; i < end; i++) {
…
// source[i].toLowerCase()
if (sourceCode >= 65 && sourceCode <= 90) { // 'A' .. 'Z'
sourceCode = sourceCode | 32;
}
if (sourceCode !== strCode) {
return false;
}
}
…
}
Case insensitive string comparison
Benefits
• Frequent comparison stops on length check
• No substring (no pressure on GC)
• No temporary strings (e.g. result of
toLowerCase/toUpperCase)
• String comparison don't pollute CG
79
Results
• RegExp
• string concatenation
• toLowerCase/toUpperCase
• substr/substring
80
No arrays in AST
What's wrong with arrays?
• As we are growing arrays their memory
fragments are to be relocated frequently
(unnecessary memory moving)
• Pressure on GC
• We don't know the size of resulting arrays
82
Solution?
83
Bi-directional list
84
85
85
AST node AST node AST node AST node
Needs a little bit more memory
than arrays, but…
86
Pros
• No memory relocation
• No GC pollution during AST assembly
• next/prev references for free
• Cheap insertion and deletion
• Better for monomorphic walkers
87
Those approaches and others allowed
to reduce memory consumption,
pressure on GC and made the parser
twice faster than before
88
89
CSSTree: 24 ms
Mensch: 31 ms
CSSOM: 36 ms
PostCSS: 38 ms
Rework: 81 ms
PostCSS Full: 100 ms
Gonzales: 175 ms
Stylecow: 176 ms
Gonzales PE: 214 ms
ParserLib: 414 ms
bootstrap.css v3.3.7 (146Kb)
github.com/postcss/benchmark
It's about this
changes
13 ms
But the story goes on 😋
90
Parser performance boost story
Part 3: а week after FrontTalks
In general
• Simplify AST structure
• Less memory consumption
• Arrays reusing
• list.map().join() -> loop + string concatenation
• and others…
92
Once more time about token costs
94
[
{
type: 46,
start: 0,
end: 1,
line: 1,
column: 1
},
…
]
Uint8Array
Uint32Array
Uint32Array
Uint32Array
Uint32Array
1 types
4 offsets
4
4 lines
4
9 per token
983 085 x 9 = 8.8Mb
lines can be computed on demand
95
96
[
{
type: 46,
start: 0,
end: 1,
line: 1,
column: 1
},
…
]
Uint8Array
Uint32Array
Uint32Array
Uint32Array
Uint32Array
1 types
4 offsets
4
4 lines
4
5 per token
983 085 x 5 = 4.9Mb
Do we really needs all 32 bits for
the offset?
Heuristics: no one parses 

more than 16Mb of CSS
97
98
offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]
type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]
99
offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]
type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]
offsetAndType[i] = type[i] << 24 | offset[i]
+
=
100
offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]
type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]
offsetAndType[i] = type[i] << 24 | offset[i]
offsetAndType = [ 16777216, 788529157, … ]
+
=
101
offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]
type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]
offsetAndType[i] = type[i] << 24 | offset[i]
offsetAndType = [ 16777216, 788529157, … ]
start = offsetAndType[i] & 0xFFFFFF;
type = offsetAndType[i] >> 24;
+
=
102
[
{
type: 46,
start: 0,
end: 1,
line: 1,
column: 1
},
…
]
Uint8Array
Uint32Array
Uint32Array
Uint32Array
Uint32Array
1 types
4 offsets
4
4 lines
4
4 per token
983 085 x 4 = 3.9Mb
3.9-7.8 Mb vs. 12.7 Mb (min)
103
104
class Scanner {
...
next() {
var next = this.currentToken + 1;
this.currentToken = next;
this.tokenStart = this.tokenEnd;
this.tokenEnd = this.offsetAndType[next + 1] & 0xFFFFFF;
this.tokenType = this.offsetAndType[next] >> 24;
}
}
Needs 2 reads for 3 values
(tokenEnd becomes tokenStart)
105
class Scanner {
...
next() {
var next = this.currentToken + 1;
this.currentToken = next;
this.tokenStart = this.tokenEnd;
this.tokenEnd = this.offsetAndType[next + 1] & 0xFFFFFF;
this.tokenType = this.offsetAndType[next] >> 24;
}
}
But 2 reads look
redundant, let's fix it…
106
offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]
type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]
offsetAndType[i] = type[i] << 24 | offset[i]
start = end
end = offsetAndType[i + 1] & 0xFFFFFF;
type = offsetAndType[i] >> 24;
106
offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]
type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]
offsetAndType[i] = type[i] << 24 | offset[i]
start = end
end = offsetAndType[i + 1] & 0xFFFFFF;
type = offsetAndType[i] >> 24;
…
107
offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]
type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]
The first offset is always zero
108
offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]
type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]
Shift offsets to the left
109
offset = [ 5, 6, 7, 9, 11, 11, …, 1234 ]
type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]
offsetAndType[i] = type[i] << 24 | offset[i + 1]
offsetAndType[i] = type[i] << 24 | offset[i]
start = end
end = offsetAndType[i] & 0xFFFFFF;
type = offsetAndType[i] >> 24;
…
110
class Scanner {
...
next() {
var next = this.currentToken + 1;
this.currentToken = next;
this.tokenStart = this.tokenEnd;
this.tokenEnd = this.offsetAndType[next] & 0xFFFFFF;
this.tokenType = this.offsetAndType[next] >> 24;
}
}
Now we need just 

one read
111
class Scanner {
...
next() {
var next = this.currentToken + 1;
this.currentToken = next;
this.tokenStart = this.tokenEnd;
next = this.offsetAndType[next];
this.tokenEnd = next & 0xFFFFFF;
this.tokenType = next >> 24;
}
}
-50% reads (~250k)
👌
Re-use
The scanner creates arrays
every time when it parses 

a new string
113
The scanner creates arrays
every time when it parses 

a new string
113
New strategy
• Preallocate 16Kb buffer by default
• Create new buffer only if current is smaller
than needed for parsing
• Significantly improves performance 

especially in cases when parsing a number of
small CSS fragments
114
115
CSSTree: 24 ms
Mensch: 31 ms
CSSOM: 36 ms
PostCSS: 38 ms
Rework: 81 ms
PostCSS Full: 100 ms
Gonzales: 175 ms
Stylecow: 176 ms
Gonzales PE: 214 ms
ParserLib: 414 ms
bootstrap.css v3.3.7 (146Kb)
github.com/postcss/benchmark
13 ms 7 ms
Current results
And still not the end… 😋
116
One more thing
CSSTree – 

is not just about performance
118
New feature*:
Parsing and matching of 

CSS values syntax
119
* Currently unique across CSS parsers
Example
120
121
csstree.github.io/docs/syntax.html
CSS syntax reference
122
csstree.github.io/docs/validator.html
CSS values validator
123
var csstree = require('css-tree');
var syntax = csstree.syntax.defaultSyntax;
var ast = csstree.parse('… your css …');
csstree.walkDeclarations(ast, function(node) {
if (!syntax.match(node.property.name, node.value)) {
console.log(syntax.lastMatchError);
}
});
Your own validator in 8 lines of code
Some tools and plugins
• csstree-validator – npm package + cli command
• stylelint-csstree-validator – plugin for stylelint
• gulp-csstree – plugin for gulp
• SublimeLinter-contrib-csstree – plugin for Sublime Text
• vscode-csstree – plugin for VS Code
• csstree-validator – plugin for Atom



More is coming…
124
Conclusion
If you want your JavaScript
works as fast as C, 

make it look like C
126
Previous talks
• CSSO – performance boost story (russian)

tinyurl.com/csso-speedup
• CSS parsing (russian)

tinyurl.com/csstree-intro
127
github.com/csstree/csstree
128
Your feedback is welcome
Roman Dvornov
@rdvornov
github.com/lahmatiy
rdvornov@gmail.com
Questions?
github.com/csstree/csstree

More Related Content

What's hot

Choosing the right parallel compute architecture
Choosing the right parallel compute architecture Choosing the right parallel compute architecture
Choosing the right parallel compute architecture corehard_by
 
marko_go_in_badoo
marko_go_in_badoomarko_go_in_badoo
marko_go_in_badooMarko Kevac
 
Евгений Крутько, Многопоточные вычисления, современный подход.
Евгений Крутько, Многопоточные вычисления, современный подход.Евгений Крутько, Многопоточные вычисления, современный подход.
Евгений Крутько, Многопоточные вычисления, современный подход.Platonov Sergey
 
Counter Wars (JEEConf 2016)
Counter Wars (JEEConf 2016)Counter Wars (JEEConf 2016)
Counter Wars (JEEConf 2016)Alexey Fyodorov
 
JRuby and Invokedynamic - Japan JUG 2015
JRuby and Invokedynamic - Japan JUG 2015JRuby and Invokedynamic - Japan JUG 2015
JRuby and Invokedynamic - Japan JUG 2015Charles Nutter
 
Øredev 2011 - JVM JIT for Dummies (What the JVM Does With Your Bytecode When ...
Øredev 2011 - JVM JIT for Dummies (What the JVM Does With Your Bytecode When ...Øredev 2011 - JVM JIT for Dummies (What the JVM Does With Your Bytecode When ...
Øredev 2011 - JVM JIT for Dummies (What the JVM Does With Your Bytecode When ...Charles Nutter
 
Down the Rabbit Hole: An Adventure in JVM Wonderland
Down the Rabbit Hole: An Adventure in JVM WonderlandDown the Rabbit Hole: An Adventure in JVM Wonderland
Down the Rabbit Hole: An Adventure in JVM WonderlandCharles Nutter
 
How to make a large C++-code base manageable
How to make a large C++-code base manageableHow to make a large C++-code base manageable
How to make a large C++-code base manageablecorehard_by
 
Fast as C: How to Write Really Terrible Java
Fast as C: How to Write Really Terrible JavaFast as C: How to Write Really Terrible Java
Fast as C: How to Write Really Terrible JavaCharles Nutter
 
Ruby Performance - The Last Mile - RubyConf India 2016
Ruby Performance - The Last Mile - RubyConf India 2016Ruby Performance - The Last Mile - RubyConf India 2016
Ruby Performance - The Last Mile - RubyConf India 2016Charles Nutter
 
Js testing
Js testingJs testing
Js testingMaslowB
 
200 Open Source Projects Later: Source Code Static Analysis Experience
200 Open Source Projects Later: Source Code Static Analysis Experience200 Open Source Projects Later: Source Code Static Analysis Experience
200 Open Source Projects Later: Source Code Static Analysis ExperienceAndrey Karpov
 
Работа с реляционными базами данных в C++
Работа с реляционными базами данных в C++Работа с реляционными базами данных в C++
Работа с реляционными базами данных в C++corehard_by
 
Node.js API pitfalls
Node.js API pitfallsNode.js API pitfalls
Node.js API pitfallsTorontoNodeJS
 
What the &~#@&lt;!? (Pointers in Rust)
What the &~#@&lt;!? (Pointers in Rust)What the &~#@&lt;!? (Pointers in Rust)
What the &~#@&lt;!? (Pointers in Rust)David Evans
 

What's hot (20)

Choosing the right parallel compute architecture
Choosing the right parallel compute architecture Choosing the right parallel compute architecture
Choosing the right parallel compute architecture
 
marko_go_in_badoo
marko_go_in_badoomarko_go_in_badoo
marko_go_in_badoo
 
Евгений Крутько, Многопоточные вычисления, современный подход.
Евгений Крутько, Многопоточные вычисления, современный подход.Евгений Крутько, Многопоточные вычисления, современный подход.
Евгений Крутько, Многопоточные вычисления, современный подход.
 
Counter Wars (JEEConf 2016)
Counter Wars (JEEConf 2016)Counter Wars (JEEConf 2016)
Counter Wars (JEEConf 2016)
 
Qt Rest Server
Qt Rest ServerQt Rest Server
Qt Rest Server
 
Down the Rabbit Hole
Down the Rabbit HoleDown the Rabbit Hole
Down the Rabbit Hole
 
JRuby and Invokedynamic - Japan JUG 2015
JRuby and Invokedynamic - Japan JUG 2015JRuby and Invokedynamic - Japan JUG 2015
JRuby and Invokedynamic - Japan JUG 2015
 
Øredev 2011 - JVM JIT for Dummies (What the JVM Does With Your Bytecode When ...
Øredev 2011 - JVM JIT for Dummies (What the JVM Does With Your Bytecode When ...Øredev 2011 - JVM JIT for Dummies (What the JVM Does With Your Bytecode When ...
Øredev 2011 - JVM JIT for Dummies (What the JVM Does With Your Bytecode When ...
 
Down the Rabbit Hole: An Adventure in JVM Wonderland
Down the Rabbit Hole: An Adventure in JVM WonderlandDown the Rabbit Hole: An Adventure in JVM Wonderland
Down the Rabbit Hole: An Adventure in JVM Wonderland
 
Groovy
GroovyGroovy
Groovy
 
How to make a large C++-code base manageable
How to make a large C++-code base manageableHow to make a large C++-code base manageable
How to make a large C++-code base manageable
 
Fast as C: How to Write Really Terrible Java
Fast as C: How to Write Really Terrible JavaFast as C: How to Write Really Terrible Java
Fast as C: How to Write Really Terrible Java
 
Ruby Performance - The Last Mile - RubyConf India 2016
Ruby Performance - The Last Mile - RubyConf India 2016Ruby Performance - The Last Mile - RubyConf India 2016
Ruby Performance - The Last Mile - RubyConf India 2016
 
Js testing
Js testingJs testing
Js testing
 
200 Open Source Projects Later: Source Code Static Analysis Experience
200 Open Source Projects Later: Source Code Static Analysis Experience200 Open Source Projects Later: Source Code Static Analysis Experience
200 Open Source Projects Later: Source Code Static Analysis Experience
 
Работа с реляционными базами данных в C++
Работа с реляционными базами данных в C++Работа с реляционными базами данных в C++
Работа с реляционными базами данных в C++
 
System Calls
System CallsSystem Calls
System Calls
 
Node.js API pitfalls
Node.js API pitfallsNode.js API pitfalls
Node.js API pitfalls
 
Living With Garbage
Living With GarbageLiving With Garbage
Living With Garbage
 
What the &~#@&lt;!? (Pointers in Rust)
What the &~#@&lt;!? (Pointers in Rust)What the &~#@&lt;!? (Pointers in Rust)
What the &~#@&lt;!? (Pointers in Rust)
 

Viewers also liked

Инструменты разные нужны, инструменты разные важны
Инструменты разные нужны, инструменты разные важныИнструменты разные нужны, инструменты разные важны
Инструменты разные нужны, инструменты разные важныRoman Dvornov
 
CSSO – compress CSS (english version)
CSSO – compress CSS (english version)CSSO – compress CSS (english version)
CSSO – compress CSS (english version)Roman Dvornov
 
DOM-шаблонизаторы – не только "быстро"
DOM-шаблонизаторы – не только "быстро"DOM-шаблонизаторы – не только "быстро"
DOM-шаблонизаторы – не только "быстро"Roman Dvornov
 
Парсим CSS: performance tips & tricks
Парсим CSS: performance tips & tricksПарсим CSS: performance tips & tricks
Парсим CSS: performance tips & tricksRoman Dvornov
 
Баба Яга против!
Баба Яга против!Баба Яга против!
Баба Яга против!Roman Dvornov
 
CSSO – история ускорения
CSSO – история ускоренияCSSO – история ускорения
CSSO – история ускоренияRoman Dvornov
 
Быстро о быстром
Быстро о быстромБыстро о быстром
Быстро о быстромRoman Dvornov
 
Как сделать ваш JavaScript быстрее
Как сделать ваш JavaScript быстрееКак сделать ваш JavaScript быстрее
Как сделать ваш JavaScript быстрееRoman Dvornov
 
CSSO — минимизируем CSS
 CSSO — минимизируем CSS CSSO — минимизируем CSS
CSSO — минимизируем CSSRoman Dvornov
 
Basis.js – «под капотом»
Basis.js – «под капотом»Basis.js – «под капотом»
Basis.js – «под капотом»Roman Dvornov
 
Не бойся, это всего лишь данные... просто их много
Не бойся, это всего лишь данные... просто их многоНе бойся, это всего лишь данные... просто их много
Не бойся, это всего лишь данные... просто их многоRoman Dvornov
 
SPA инструменты
SPA инструментыSPA инструменты
SPA инструментыRoman Dvornov
 
Карточный домик
Карточный домикКарточный домик
Карточный домикRoman Dvornov
 
Remote (dev)tools своими руками
Remote (dev)tools своими рукамиRemote (dev)tools своими руками
Remote (dev)tools своими рукамиRoman Dvornov
 
Жизнь в изоляции
Жизнь в изоляцииЖизнь в изоляции
Жизнь в изоляцииRoman Dvornov
 

Viewers also liked (16)

Инструменты разные нужны, инструменты разные важны
Инструменты разные нужны, инструменты разные важныИнструменты разные нужны, инструменты разные важны
Инструменты разные нужны, инструменты разные важны
 
CSSO – compress CSS (english version)
CSSO – compress CSS (english version)CSSO – compress CSS (english version)
CSSO – compress CSS (english version)
 
DOM-шаблонизаторы – не только "быстро"
DOM-шаблонизаторы – не только "быстро"DOM-шаблонизаторы – не только "быстро"
DOM-шаблонизаторы – не только "быстро"
 
Парсим CSS: performance tips & tricks
Парсим CSS: performance tips & tricksПарсим CSS: performance tips & tricks
Парсим CSS: performance tips & tricks
 
Баба Яга против!
Баба Яга против!Баба Яга против!
Баба Яга против!
 
Парсим CSS
Парсим CSSПарсим CSS
Парсим CSS
 
CSSO – история ускорения
CSSO – история ускоренияCSSO – история ускорения
CSSO – история ускорения
 
Быстро о быстром
Быстро о быстромБыстро о быстром
Быстро о быстром
 
Как сделать ваш JavaScript быстрее
Как сделать ваш JavaScript быстрееКак сделать ваш JavaScript быстрее
Как сделать ваш JavaScript быстрее
 
CSSO — минимизируем CSS
 CSSO — минимизируем CSS CSSO — минимизируем CSS
CSSO — минимизируем CSS
 
Basis.js – «под капотом»
Basis.js – «под капотом»Basis.js – «под капотом»
Basis.js – «под капотом»
 
Не бойся, это всего лишь данные... просто их много
Не бойся, это всего лишь данные... просто их многоНе бойся, это всего лишь данные... просто их много
Не бойся, это всего лишь данные... просто их много
 
SPA инструменты
SPA инструментыSPA инструменты
SPA инструменты
 
Карточный домик
Карточный домикКарточный домик
Карточный домик
 
Remote (dev)tools своими руками
Remote (dev)tools своими рукамиRemote (dev)tools своими руками
Remote (dev)tools своими руками
 
Жизнь в изоляции
Жизнь в изоляцииЖизнь в изоляции
Жизнь в изоляции
 

Similar to CSS Parsing Performance Tips

Tokyo APAC Groundbreakers tour - The Complete Java Developer
Tokyo APAC Groundbreakers tour - The Complete Java DeveloperTokyo APAC Groundbreakers tour - The Complete Java Developer
Tokyo APAC Groundbreakers tour - The Complete Java DeveloperConnor McDonald
 
#GDC15 Code Clinic
#GDC15 Code Clinic#GDC15 Code Clinic
#GDC15 Code ClinicMike Acton
 
Static analysis and writing C/C++ of high quality code for embedded systems
Static analysis and writing C/C++ of high quality code for embedded systemsStatic analysis and writing C/C++ of high quality code for embedded systems
Static analysis and writing C/C++ of high quality code for embedded systemsAndrey Karpov
 
Gotcha! Ruby things that will come back to bite you.
Gotcha! Ruby things that will come back to bite you.Gotcha! Ruby things that will come back to bite you.
Gotcha! Ruby things that will come back to bite you.David Tollmyr
 
Refactor legacy code through pure functions
Refactor legacy code through pure functionsRefactor legacy code through pure functions
Refactor legacy code through pure functionsAlexandru Bolboaca
 
Nodejs性能分析优化和分布式设计探讨
Nodejs性能分析优化和分布式设计探讨Nodejs性能分析优化和分布式设计探讨
Nodejs性能分析优化和分布式设计探讨flyinweb
 
Csw2016 wheeler barksdale-gruskovnjak-execute_mypacket
Csw2016 wheeler barksdale-gruskovnjak-execute_mypacketCsw2016 wheeler barksdale-gruskovnjak-execute_mypacket
Csw2016 wheeler barksdale-gruskovnjak-execute_mypacketCanSecWest
 
Inside the JVM - Follow the white rabbit! / Breizh JUG
Inside the JVM - Follow the white rabbit! / Breizh JUGInside the JVM - Follow the white rabbit! / Breizh JUG
Inside the JVM - Follow the white rabbit! / Breizh JUGSylvain Wallez
 
React Native Evening
React Native EveningReact Native Evening
React Native EveningTroy Miles
 
Code instrumentation
Code instrumentationCode instrumentation
Code instrumentationBryan Reinero
 
Introduction to web programming for java and c# programmers by @drpicox
Introduction to web programming for java and c# programmers by @drpicoxIntroduction to web programming for java and c# programmers by @drpicox
Introduction to web programming for java and c# programmers by @drpicoxDavid Rodenas
 
NativeBoost
NativeBoostNativeBoost
NativeBoostESUG
 
Deep Learning in Spark with BigDL by Petar Zecevic at Big Data Spain 2017
Deep Learning in Spark with BigDL by Petar Zecevic at Big Data Spain 2017Deep Learning in Spark with BigDL by Petar Zecevic at Big Data Spain 2017
Deep Learning in Spark with BigDL by Petar Zecevic at Big Data Spain 2017Big Data Spain
 
Boost delivery stream with code discipline engineering
Boost delivery stream with code discipline engineeringBoost delivery stream with code discipline engineering
Boost delivery stream with code discipline engineeringMiro Wengner
 
Ekon24 from Delphi to AVX2
Ekon24 from Delphi to AVX2Ekon24 from Delphi to AVX2
Ekon24 from Delphi to AVX2Arnaud Bouchez
 
CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak
CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak   CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak
CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak PROIDEA
 
Data oriented design and c++
Data oriented design and c++Data oriented design and c++
Data oriented design and c++Mike Acton
 
Sangam 18 - Database Development: Return of the SQL Jedi
Sangam 18 - Database Development: Return of the SQL JediSangam 18 - Database Development: Return of the SQL Jedi
Sangam 18 - Database Development: Return of the SQL JediConnor McDonald
 
Numerical tour in the Python eco-system: Python, NumPy, scikit-learn
Numerical tour in the Python eco-system: Python, NumPy, scikit-learnNumerical tour in the Python eco-system: Python, NumPy, scikit-learn
Numerical tour in the Python eco-system: Python, NumPy, scikit-learnArnaud Joly
 

Similar to CSS Parsing Performance Tips (20)

Tokyo APAC Groundbreakers tour - The Complete Java Developer
Tokyo APAC Groundbreakers tour - The Complete Java DeveloperTokyo APAC Groundbreakers tour - The Complete Java Developer
Tokyo APAC Groundbreakers tour - The Complete Java Developer
 
#GDC15 Code Clinic
#GDC15 Code Clinic#GDC15 Code Clinic
#GDC15 Code Clinic
 
Static analysis and writing C/C++ of high quality code for embedded systems
Static analysis and writing C/C++ of high quality code for embedded systemsStatic analysis and writing C/C++ of high quality code for embedded systems
Static analysis and writing C/C++ of high quality code for embedded systems
 
Gotcha! Ruby things that will come back to bite you.
Gotcha! Ruby things that will come back to bite you.Gotcha! Ruby things that will come back to bite you.
Gotcha! Ruby things that will come back to bite you.
 
Modern C++
Modern C++Modern C++
Modern C++
 
Refactor legacy code through pure functions
Refactor legacy code through pure functionsRefactor legacy code through pure functions
Refactor legacy code through pure functions
 
Nodejs性能分析优化和分布式设计探讨
Nodejs性能分析优化和分布式设计探讨Nodejs性能分析优化和分布式设计探讨
Nodejs性能分析优化和分布式设计探讨
 
Csw2016 wheeler barksdale-gruskovnjak-execute_mypacket
Csw2016 wheeler barksdale-gruskovnjak-execute_mypacketCsw2016 wheeler barksdale-gruskovnjak-execute_mypacket
Csw2016 wheeler barksdale-gruskovnjak-execute_mypacket
 
Inside the JVM - Follow the white rabbit! / Breizh JUG
Inside the JVM - Follow the white rabbit! / Breizh JUGInside the JVM - Follow the white rabbit! / Breizh JUG
Inside the JVM - Follow the white rabbit! / Breizh JUG
 
React Native Evening
React Native EveningReact Native Evening
React Native Evening
 
Code instrumentation
Code instrumentationCode instrumentation
Code instrumentation
 
Introduction to web programming for java and c# programmers by @drpicox
Introduction to web programming for java and c# programmers by @drpicoxIntroduction to web programming for java and c# programmers by @drpicox
Introduction to web programming for java and c# programmers by @drpicox
 
NativeBoost
NativeBoostNativeBoost
NativeBoost
 
Deep Learning in Spark with BigDL by Petar Zecevic at Big Data Spain 2017
Deep Learning in Spark with BigDL by Petar Zecevic at Big Data Spain 2017Deep Learning in Spark with BigDL by Petar Zecevic at Big Data Spain 2017
Deep Learning in Spark with BigDL by Petar Zecevic at Big Data Spain 2017
 
Boost delivery stream with code discipline engineering
Boost delivery stream with code discipline engineeringBoost delivery stream with code discipline engineering
Boost delivery stream with code discipline engineering
 
Ekon24 from Delphi to AVX2
Ekon24 from Delphi to AVX2Ekon24 from Delphi to AVX2
Ekon24 from Delphi to AVX2
 
CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak
CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak   CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak
CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak
 
Data oriented design and c++
Data oriented design and c++Data oriented design and c++
Data oriented design and c++
 
Sangam 18 - Database Development: Return of the SQL Jedi
Sangam 18 - Database Development: Return of the SQL JediSangam 18 - Database Development: Return of the SQL Jedi
Sangam 18 - Database Development: Return of the SQL Jedi
 
Numerical tour in the Python eco-system: Python, NumPy, scikit-learn
Numerical tour in the Python eco-system: Python, NumPy, scikit-learnNumerical tour in the Python eco-system: Python, NumPy, scikit-learn
Numerical tour in the Python eco-system: Python, NumPy, scikit-learn
 

More from Roman Dvornov

Unit-тестирование скриншотами: преодолеваем звуковой барьер
Unit-тестирование скриншотами: преодолеваем звуковой барьерUnit-тестирование скриншотами: преодолеваем звуковой барьер
Unit-тестирование скриншотами: преодолеваем звуковой барьерRoman Dvornov
 
Масштабируемая архитектура фронтенда
Масштабируемая архитектура фронтендаМасштабируемая архитектура фронтенда
Масштабируемая архитектура фронтендаRoman Dvornov
 
CSS глазами машин
CSS глазами машинCSS глазами машин
CSS глазами машинRoman Dvornov
 
My Open Source (Sept 2017)
My Open Source (Sept 2017)My Open Source (Sept 2017)
My Open Source (Sept 2017)Roman Dvornov
 
Rempl – крутая платформа для крутых инструментов
Rempl – крутая платформа для крутых инструментовRempl – крутая платформа для крутых инструментов
Rempl – крутая платформа для крутых инструментовRoman Dvornov
 
CSSO — сжимаем CSS (часть 2)
CSSO — сжимаем CSS (часть 2)CSSO — сжимаем CSS (часть 2)
CSSO — сжимаем CSS (часть 2)Roman Dvornov
 
Инструментируй это
Инструментируй этоИнструментируй это
Инструментируй этоRoman Dvornov
 
Как построить DOM
Как построить DOMКак построить DOM
Как построить DOMRoman Dvornov
 
Компонентный подход: скучно, неинтересно, бесперспективно
Компонентный подход: скучно, неинтересно, бесперспективноКомпонентный подход: скучно, неинтересно, бесперспективно
Компонентный подход: скучно, неинтересно, бесперспективноRoman Dvornov
 
Basis.js - почему я не бросил разрабатывать свой фреймворк (extended)
Basis.js - почему я не бросил разрабатывать свой фреймворк (extended)Basis.js - почему я не бросил разрабатывать свой фреймворк (extended)
Basis.js - почему я не бросил разрабатывать свой фреймворк (extended)Roman Dvornov
 
basis.js - почему я не бросил разрабатывать свой фреймворк
basis.js - почему я не бросил разрабатывать свой фреймворкbasis.js - почему я не бросил разрабатывать свой фреймворк
basis.js - почему я не бросил разрабатывать свой фреймворкRoman Dvornov
 

More from Roman Dvornov (12)

Unit-тестирование скриншотами: преодолеваем звуковой барьер
Unit-тестирование скриншотами: преодолеваем звуковой барьерUnit-тестирование скриншотами: преодолеваем звуковой барьер
Unit-тестирование скриншотами: преодолеваем звуковой барьер
 
Масштабируемая архитектура фронтенда
Масштабируемая архитектура фронтендаМасштабируемая архитектура фронтенда
Масштабируемая архитектура фронтенда
 
CSS глазами машин
CSS глазами машинCSS глазами машин
CSS глазами машин
 
My Open Source (Sept 2017)
My Open Source (Sept 2017)My Open Source (Sept 2017)
My Open Source (Sept 2017)
 
Rempl – крутая платформа для крутых инструментов
Rempl – крутая платформа для крутых инструментовRempl – крутая платформа для крутых инструментов
Rempl – крутая платформа для крутых инструментов
 
CSSO — сжимаем CSS (часть 2)
CSSO — сжимаем CSS (часть 2)CSSO — сжимаем CSS (часть 2)
CSSO — сжимаем CSS (часть 2)
 
Component Inspector
Component InspectorComponent Inspector
Component Inspector
 
Инструментируй это
Инструментируй этоИнструментируй это
Инструментируй это
 
Как построить DOM
Как построить DOMКак построить DOM
Как построить DOM
 
Компонентный подход: скучно, неинтересно, бесперспективно
Компонентный подход: скучно, неинтересно, бесперспективноКомпонентный подход: скучно, неинтересно, бесперспективно
Компонентный подход: скучно, неинтересно, бесперспективно
 
Basis.js - почему я не бросил разрабатывать свой фреймворк (extended)
Basis.js - почему я не бросил разрабатывать свой фреймворк (extended)Basis.js - почему я не бросил разрабатывать свой фреймворк (extended)
Basis.js - почему я не бросил разрабатывать свой фреймворк (extended)
 
basis.js - почему я не бросил разрабатывать свой фреймворк
basis.js - почему я не бросил разрабатывать свой фреймворкbasis.js - почему я не бросил разрабатывать свой фреймворк
basis.js - почему я не бросил разрабатывать свой фреймворк
 

Recently uploaded

2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 

Recently uploaded (20)

2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 

CSS Parsing Performance Tips

  • 1. CSS Parsing performance tips & tricks Roman Dvornov Avito Moscow, September 2016
  • 2. Frontend lead in Avito Specializes in SPA Maintainer of:
 basis.js, CSSO, 
 component-inspector, 
 csstree and others
  • 5. CSSTree – 
 fastest detailed CSS parser 5
  • 6. How this project was born
  • 7. About a year ago I started 
 to maintain CSSO (a CSS minifier) 7 github.com/css/csso
  • 8. CSSO was based on Gonzales (a CSS parser) 8 github.com/css/gonzales
  • 9. What's wrong with Gonzales • Development stopped in 2013 • Unhandy and buggy AST format • Parsing mistakes • Excessively complex code base • Slow, high memory consumption, pressure for GC 9
  • 10. But I didn’t want 
 to spend my time developing the parser… 10
  • 12. You can find a lot of CSS parsers 12
  • 13. Common problems • Not developing currently • Outdated (don't support latest CSS features) • Buggy • Unhandy AST • Slow 13
  • 14. PostCSS parser is a good choice if you need one now 14 postcss.org
  • 15. PostCSS pros • Сonstantly developing • Parses CSS well, even non-standard syntax 
 + tolerant mode • Saves formatting info • Handy API to work with AST • Fast 15
  • 16. General con: selectors and values are not parsed (are represented as strings) 16
  • 17. That forces developers to • Use non-robust or non-effective approaches • Invent their own parsers • Use additional parsers:
 postcss-selector-parser
 postcss-value-parser 17
  • 18. Switching to PostCSS meant writing 
 our own selector and value parsers, what is pretty much the same as writing an entirely new parser 18
  • 19. However, as a result of a continuous refactoring within a few months 
 the CSSO parser was completely rewrote (which was not planned) 19
  • 20. And was extracted 
 to a separate project github.com/csstree/csstree 20
  • 22. CSSO – performance boost story (russian) 22 tinyurl.com/csso-speedup My previous talk about parser performance
  • 23. After my talk on HolyJS conference the parser's performance was improved 
 one more time :) 23 * Thanks Vyacheslav @mraleph Egorov for inspiration
  • 24. 24 CSSTree: 24 ms Mensch: 31 ms CSSOM: 36 ms PostCSS: 38 ms Rework: 81 ms PostCSS Full: 100 ms Gonzales: 175 ms Stylecow: 176 ms Gonzales PE: 214 ms ParserLib: 414 ms bootstrap.css v3.3.7 (146Kb) github.com/postcss/benchmark Non-detailed AST Detailed AST PostCSS Full = + postcss-selector-parser + postcss-value-parser
  • 25. Epic fail as I realised later I extracted 
 the wrong version of the parser 25 😱 github.com/csstree/csstree/commit/57568c758195153e337f6154874c3bc42dd04450
  • 26. 26 CSSTree: 24 ms Mensch: 31 ms CSSOM: 36 ms PostCSS: 38 ms Rework: 81 ms PostCSS Full: 100 ms Gonzales: 175 ms Stylecow: 176 ms Gonzales PE: 214 ms ParserLib: 414 ms bootstrap.css v3.3.7 (146Kb) github.com/postcss/benchmark Time after parser update 13 ms
  • 28. Main steps • Tokenization • Tree assembling 28
  • 30. 30 • whitespaces – [ nrtf]+ • keyword – [a-zA-aZ…]+ • number – [0-9]+ • string – "string" or 'string' • comment – /* comment */ • punctuation – [;,.#{}[]()…] Split text into tokens
  • 31. 31 .foo { width: 10px; } [ '.', 'foo', ' ', '{', 'n ', 'width', ':', ' ', '10', 'px', ';', 'n', '}' ]
  • 32. We need more info about every token: type and location 32 It is more efficient 
 to compute type and location on tokenization step
  • 33. 33 .foo { width: 10px; } [ { type: 'FullStop', value: '.', offset: 0, line: 1, column: 1 }, … ]
  • 35. 35 function getSelector() { var selector = { type: 'Selector', sequence: [] }; // main loop return selector; } Creating a node
  • 36. 36 for (;currentToken < tokenCount; currentToken++) { switch (tokens[currentToken]) { case TokenType.Hash: // # selector.sequence.push(getId()); break; case TokenType.FullStop: // . selector.sequence.push(getClass()); break; … } Main loop
  • 37. 37 { "type": "StyleSheet", "rules": [{ "type": "Atrule", "name": "import", "expression": { "type": "AtruleExpression", "sequence": [ ... ] }, "block": null }] } Result
  • 38. Parser performance boost Part 2: new horizons
  • 39. 39 [ { type: 'FullStop', value: '.', offset: 0, line: 1, column: 1 }, … ] Token's cost: 24 + 5 * 4 + array = min 50 bytes per token Our project ~1Mb CSS 254 062 tokens = min 12.7 Mb
  • 40. Out of the box: changing approach
  • 41. Compute all tokens at once and then assembly a tree is much more easy, but needs more memory, therefore is slower 41
  • 43. 43 scanner.token // current token or null scanner.next() // going to next token scanner.lookup(N) // look ahead, returns // Nth token from current token Key API
  • 44. 44 • lookup(N)
 fills tokens buffer up to N tokens (if they are not computed yet), returns N-1 token from buffer • next()
 shift token from buffer, if any, or compute 
 next token
  • 45. Computing the same number of tokens, 
 but not simultaneously 
 and requires less memory 45
  • 46. Problem: the approach puts pressure on GC 46
  • 48. 48 [ { type: 'FullStop', value: '.', offset: 0, line: 1, column: 1 }, … ] Type as string is easy to understand, but it's for internal use only and we can replace it by numbers
  • 49. 49 [ { type: FULLSTOP, value: '.', offset: 0, line: 1, column: 1 }, … ] … // '.'.charCodeAt(0) var FULLSTOP = 46; …
  • 50. 50 [ { type: 46, value: '.', offset: 0, line: 1, column: 1 }, … ]
  • 51. 51 [ { type: 46, value: '.', offset: 0, line: 1, column: 1 }, … ] We can avoid substring storage in the token – it's very expensive for punctuation (moreover those substrings are never used); Many constructions are assembled by several substrings. One long substring is better than 
 a concat of several small ones
  • 52. 52 [ { type: 46, value: '.', offset: 0, line: 1, column: 1 }, … ] [ { type: 46, start: 0, end: 1, line: 1, column: 1 }, … ]
  • 53. 53 [ { type: 46, start: 0, end: 1, line: 1, column: 1 }, … ] Look, Ma! No strings just numbers!
  • 54. 54 Moreover not an Array, but TypedArray Array 
 of objects Arrays
 of numbers
  • 55. Array vs. TypedArray • Can't have holes • Faster in theory (less checking) • Can be stored outside the heap (when big enough) • Prefilled with zeros 55
  • 56. 56 [ { type: 46, start: 0, end: 1, line: 1, column: 1 }, … ] Uint8Array Uint32Array Uint32Array Uint32Array Uint32Array 1 4 4 4 4 17 per token (tokens count) 254 062 x 17 = 4.3Mb
  • 58. Houston we have a problem: TypedArray has a fixed length,
 but we don't know how many tokens will be found 58
  • 59. 59 [ { type: 46, start: 0, end: 1, line: 1, column: 1 }, … ] Uint8Array Uint32Array Uint32Array Uint32Array Uint32Array 1 4 4 4 4 17 per token (symbols count) 983 085 x 17 = 16.7Mb
  • 60. 16.7Mb vs. 12.7Mb (min) 60
  • 61. 16.7Mb vs. 12.7Mb (min) 60 Don't give up, 
 let's look on arrays more attentively
  • 62. 61 start = [ 0, 5, 6, 7, 9, 11, …, 35 ] end = [ 5, 6, 7, 9, 11, 12, …, 36 ]
  • 63. 61 start = [ 0, 5, 6, 7, 9, 11, …, 35 ] end = [ 5, 6, 7, 9, 11, 12, …, 36 ] …
  • 64. 62 start = [ 0, 5, 6, 7, 9, 11, …, 35 ] end = [ 5, 6, 7, 9, 11, 12, …, 36 ] offset = [ 0, 5, 6, 7, 9, 11, …, 35, 36 ] start = offset[i] end = offset[i + 1] + =
  • 65. 63 [ { type: 46, start: 0, end: 1, line: 1, column: 1 }, … ] Uint8Array Uint32Array Uint32Array Uint32Array Uint32Array 1 4 4 4 4 13 per token 983 085 x 13 = 12.7Mb
  • 66. 64 a { top: 0; } lines = [ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3 ] columns = [ 1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1 ] lines & columns
  • 67. 64 a { top: 0; } lines = [ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3 ] columns = [ 1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1 ] lines & columns
  • 68. 65 line = lines[offset]; column = offset - lines.lastIndexOf(line - 1, offset); lines & columns
  • 69. 65 line = lines[offset]; column = offset - lines.lastIndexOf(line - 1, offset); lines & columns It's acceptable only for short lines, that's why we cache the last line start offset
  • 70. 66 [ { type: 46, start: 0, end: 1, line: 1, column: 1 }, … ] Uint8Array Uint32Array Uint32Array Uint32Array Uint32Array 1 4 4 4 4 9 per token 983 085 x 9 = 8.8Mb
  • 73. Performance «killers»* • RegExp • String concatenation • toLowerCase/toUpperCase • substr/substring • … 69 * Polluted GC pulls performance down
  • 74. Performance «killers»* • RegExp • String concatenation • toLowerCase/toUpperCase • substr/substring • … 70 * Polluted GC pulls performance down We can’t avoid using these things, but we can get rid of the rest
  • 75. 71 var start = scanner.tokenStart; … scanner.next(); … scanner.next(); … return source.substr(start, scanner.tokenEnd); Avoid string concatenations
  • 76. 72 function cmpStr(source, start, end, str) { if (end - start !== str.length) { return false; } for (var i = start; i < end; i++) { var sourceCode = source.charCodeAt(i); var strCode = str.charCodeAt(i - start); if (sourceCode !== strCode) { return false; } } return true; } String comparison No substring!
  • 77. 73 function cmpStr(source, start, end, str) { if (end - start !== str.length) { return false; } for (var i = start; i < end; i++) { var sourceCode = source.charCodeAt(i); var strCode = str.charCodeAt(i - start); if (sourceCode !== strCode) { return false; } } return true; } String comparison Length fast-check
  • 78. 74 function cmpStr(source, start, end, str) { if (end - start !== str.length) { return false; } for (var i = start; i < end; i++) { var sourceCode = source.charCodeAt(i); var strCode = str.charCodeAt(i - start); if (sourceCode !== strCode) { return false; } } return true; } String comparison Compare strings 
 by char codes
  • 79. Case insensitive comparison of strings*? 75 * Means avoid toLowerCase/toUpperCase
  • 80. Heuristics • Comparison with the reference strings only (str) • Reference strings may be in lower case and contain latin letters only (no unicode) • I read once on Twitter… 76
  • 81. Setting of the 6th bit to 1 changes upper case latin letter to lower case (works for latin ASCII letters only) 'A' = 01000001 'a' = 01100001 'A'.charCodeAt(0) | 32 === 'a'.charCodeAt(0) 77
  • 82. 78 function cmpStr(source, start, end, str) { … for (var i = start; i < end; i++) { … // source[i].toLowerCase() if (sourceCode >= 65 && sourceCode <= 90) { // 'A' .. 'Z' sourceCode = sourceCode | 32; } if (sourceCode !== strCode) { return false; } } … } Case insensitive string comparison
  • 83. Benefits • Frequent comparison stops on length check • No substring (no pressure on GC) • No temporary strings (e.g. result of toLowerCase/toUpperCase) • String comparison don't pollute CG 79
  • 84. Results • RegExp • string concatenation • toLowerCase/toUpperCase • substr/substring 80
  • 86. What's wrong with arrays? • As we are growing arrays their memory fragments are to be relocated frequently (unnecessary memory moving) • Pressure on GC • We don't know the size of resulting arrays 82
  • 89. 85
  • 90. 85 AST node AST node AST node AST node
  • 91. Needs a little bit more memory than arrays, but… 86
  • 92. Pros • No memory relocation • No GC pollution during AST assembly • next/prev references for free • Cheap insertion and deletion • Better for monomorphic walkers 87
  • 93. Those approaches and others allowed to reduce memory consumption, pressure on GC and made the parser twice faster than before 88
  • 94. 89 CSSTree: 24 ms Mensch: 31 ms CSSOM: 36 ms PostCSS: 38 ms Rework: 81 ms PostCSS Full: 100 ms Gonzales: 175 ms Stylecow: 176 ms Gonzales PE: 214 ms ParserLib: 414 ms bootstrap.css v3.3.7 (146Kb) github.com/postcss/benchmark It's about this changes 13 ms
  • 95. But the story goes on 😋 90
  • 96. Parser performance boost story Part 3: а week after FrontTalks
  • 97. In general • Simplify AST structure • Less memory consumption • Arrays reusing • list.map().join() -> loop + string concatenation • and others… 92
  • 98. Once more time about token costs
  • 99. 94 [ { type: 46, start: 0, end: 1, line: 1, column: 1 }, … ] Uint8Array Uint32Array Uint32Array Uint32Array Uint32Array 1 types 4 offsets 4 4 lines 4 9 per token 983 085 x 9 = 8.8Mb
  • 100. lines can be computed on demand 95
  • 101. 96 [ { type: 46, start: 0, end: 1, line: 1, column: 1 }, … ] Uint8Array Uint32Array Uint32Array Uint32Array Uint32Array 1 types 4 offsets 4 4 lines 4 5 per token 983 085 x 5 = 4.9Mb
  • 102. Do we really needs all 32 bits for the offset? Heuristics: no one parses 
 more than 16Mb of CSS 97
  • 103. 98 offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ] type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]
  • 104. 99 offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ] type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ] offsetAndType[i] = type[i] << 24 | offset[i] + =
  • 105. 100 offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ] type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ] offsetAndType[i] = type[i] << 24 | offset[i] offsetAndType = [ 16777216, 788529157, … ] + =
  • 106. 101 offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ] type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ] offsetAndType[i] = type[i] << 24 | offset[i] offsetAndType = [ 16777216, 788529157, … ] start = offsetAndType[i] & 0xFFFFFF; type = offsetAndType[i] >> 24; + =
  • 107. 102 [ { type: 46, start: 0, end: 1, line: 1, column: 1 }, … ] Uint8Array Uint32Array Uint32Array Uint32Array Uint32Array 1 types 4 offsets 4 4 lines 4 4 per token 983 085 x 4 = 3.9Mb
  • 108. 3.9-7.8 Mb vs. 12.7 Mb (min) 103
  • 109. 104 class Scanner { ... next() { var next = this.currentToken + 1; this.currentToken = next; this.tokenStart = this.tokenEnd; this.tokenEnd = this.offsetAndType[next + 1] & 0xFFFFFF; this.tokenType = this.offsetAndType[next] >> 24; } } Needs 2 reads for 3 values (tokenEnd becomes tokenStart)
  • 110. 105 class Scanner { ... next() { var next = this.currentToken + 1; this.currentToken = next; this.tokenStart = this.tokenEnd; this.tokenEnd = this.offsetAndType[next + 1] & 0xFFFFFF; this.tokenType = this.offsetAndType[next] >> 24; } } But 2 reads look redundant, let's fix it…
  • 111. 106 offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ] type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ] offsetAndType[i] = type[i] << 24 | offset[i] start = end end = offsetAndType[i + 1] & 0xFFFFFF; type = offsetAndType[i] >> 24;
  • 112. 106 offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ] type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ] offsetAndType[i] = type[i] << 24 | offset[i] start = end end = offsetAndType[i + 1] & 0xFFFFFF; type = offsetAndType[i] >> 24; …
  • 113. 107 offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ] type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ] The first offset is always zero
  • 114. 108 offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ] type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ] Shift offsets to the left
  • 115. 109 offset = [ 5, 6, 7, 9, 11, 11, …, 1234 ] type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ] offsetAndType[i] = type[i] << 24 | offset[i + 1] offsetAndType[i] = type[i] << 24 | offset[i] start = end end = offsetAndType[i] & 0xFFFFFF; type = offsetAndType[i] >> 24; …
  • 116. 110 class Scanner { ... next() { var next = this.currentToken + 1; this.currentToken = next; this.tokenStart = this.tokenEnd; this.tokenEnd = this.offsetAndType[next] & 0xFFFFFF; this.tokenType = this.offsetAndType[next] >> 24; } } Now we need just 
 one read
  • 117. 111 class Scanner { ... next() { var next = this.currentToken + 1; this.currentToken = next; this.tokenStart = this.tokenEnd; next = this.offsetAndType[next]; this.tokenEnd = next & 0xFFFFFF; this.tokenType = next >> 24; } } -50% reads (~250k) 👌
  • 118. Re-use
  • 119. The scanner creates arrays every time when it parses 
 a new string 113
  • 120. The scanner creates arrays every time when it parses 
 a new string 113
  • 121. New strategy • Preallocate 16Kb buffer by default • Create new buffer only if current is smaller than needed for parsing • Significantly improves performance 
 especially in cases when parsing a number of small CSS fragments 114
  • 122. 115 CSSTree: 24 ms Mensch: 31 ms CSSOM: 36 ms PostCSS: 38 ms Rework: 81 ms PostCSS Full: 100 ms Gonzales: 175 ms Stylecow: 176 ms Gonzales PE: 214 ms ParserLib: 414 ms bootstrap.css v3.3.7 (146Kb) github.com/postcss/benchmark 13 ms 7 ms Current results
  • 123. And still not the end… 😋 116
  • 125. CSSTree – 
 is not just about performance 118
  • 126. New feature*: Parsing and matching of 
 CSS values syntax 119 * Currently unique across CSS parsers
  • 130. 123 var csstree = require('css-tree'); var syntax = csstree.syntax.defaultSyntax; var ast = csstree.parse('… your css …'); csstree.walkDeclarations(ast, function(node) { if (!syntax.match(node.property.name, node.value)) { console.log(syntax.lastMatchError); } }); Your own validator in 8 lines of code
  • 131. Some tools and plugins • csstree-validator – npm package + cli command • stylelint-csstree-validator – plugin for stylelint • gulp-csstree – plugin for gulp • SublimeLinter-contrib-csstree – plugin for Sublime Text • vscode-csstree – plugin for VS Code • csstree-validator – plugin for Atom
 
 More is coming… 124
  • 133. If you want your JavaScript works as fast as C, 
 make it look like C 126
  • 134. Previous talks • CSSO – performance boost story (russian)
 tinyurl.com/csso-speedup • CSS parsing (russian)
 tinyurl.com/csstree-intro 127