SlideShare uma empresa Scribd logo
1 de 136
Baixar para ler offline
Парсим CSS
performance tips & tricks
Роман Дворнов
Avito
Москва, сентябрь 2016
Руководитель 

фронтенда в Avito
Основной интерес – SPA
Open source:

basis.js, CSSO, 

component-inspector, 

csstree и другие
За любую движуху, 

кроме голодовки ;)
Парсим CSS
(зачем? почему? как дальше жить?)
3
tinyurl.com/csstree-intro
Начало истории (доклад)
CSSTree
CSSTree – самый быстрый 

и детальный парсер CSS
5
Как я до этого докатился?
Чуть меньше года назад 

я стал мейнтейнером CSSO
(минификатор CSS)
7
github.com/css/csso
CSSO работал на основе
парсера Gonzales
8
github.com/css/gonzales
Проблемы
• Не развивается с 2013
• Неудобный формат AST, местами странный
• Много ошибок
• Запутанная и сложная кодовая база
• Медленный, потребляет много памяти, GC
9
Парсер – последнее, что я
собирался трогать…
10
Альтернатива?
Парсеров CSS на JavaScript
достаточно много
12
Частые проблемы
• Заброшены и не развиваются
• Устарели (не поддерживают новое в CSS)
• Содержат ошибки
• Неудачная структура
• Медленные
13
Наилучшим выбором может
быть парсер из PostCSS
14
postcss.org
Плюсы PostCSS
• Развивается и поддерживается
• Хорошо справляется с синтаксисом CSS и даже
будущим + tolerant mode
• Сохраняет информацию о форматировании
• Удобное API для работы с AST
• Быстрый
15
Основная проблема:
селекторы и значения свойств
остаются не разобранными
(хранятся в виде строки)
16
Это вынуждает разработчиков
• Использовать костыли
• Писать свои парсеры
• Использовать дополнительные парсеры:

postcss-selector-parser

postcss-value-parser
17
Переход на PostCSS означал написание
собственных парсеров селекторов и
свойств, что не сильно отличается от
написания парсера целиком
18
Регулярный рефакторинг приводит к тому,
что парсер может быть полностью переписан 

(это норма 😳)
19
Парсер выделен в отдельный проект
github.com/csstree/csstree
20
Скорость
CSSO – история ускорения
(в том числе про парсер)
22
tinyurl.com/csso-speedup
В предыдущих сериях (доклад)
После выступления разогнал
парсер еще :)
23
* Вдохновленный общением с Вячеславом @mraleph Егоровым
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
Не детальное AST
Детальное AST
PostCSS Full =
+ postcss-selector-parser
+ postcss-value-parser
Epic fail
как выяснилось позже, я вынес
не ту версию парсера
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
На FrontTalks был
показан результат
до разгона
13 ms
Парсеры: курс молодого бойца
Основные шаги
• Токенизация
• Построение дерева (лексер)
28
Токенизация
30
• whitespaces – [ nrtf]+
• keyword – [a-zA-aZ…]+
• number – [0-9]+
• string – "string" или 'string'
• comment – /* comment */
• punctuation – [;,.#{}[]()…]
Разбиение текста на токены
31
.foo {
width: 10px;
}
[
'.', 'foo', ' ', '{',
'n ', 'width', ':',
' ', '10', 'px', ';',
'n', '}'
]
Нужна дополнительная информация
о токене: тип и локация
32
На этапе токенизации мы
знаем тип и позицию,
считать их после – дорого
33
.foo {
width: 10px;
}
[
{
type: 'FullStop',
value: '.',
offset: 0,
line: 1,
column: 1
},
…
]
Сборка
35
function getSelector() {
var selector = {
type: 'Selector',
sequence: []
};
// main loop
return selector;
}
Сборка
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
}]
}
Результат
История ускорения #2
39
[
{
type: 'FullStop',
value: '.',
offset: 0,
line: 1,
column: 1
},
…
]
Стоимость токена:
24 + 5 * 4 + массив =
min 50 bytes per token
В нашем проекте ~1Mb CSS
254 062 токена
=
min 12.7 Mb
Прелюдия: меняем подход
Посчитать все токены, а потом 

из них собирать AST – проще,
но ведет к лишним затратам памяти
и медленней
41
Scanner
(ленивый токенайзер)
42
43
scanner.token // текущий токен или null
scanner.next() // переход к следующему токену
scanner.lookup(N) // заглядывание вперед, возвращает
// токен на N-ой позиции от текущей
Основное API
44
• lookup(N)

заполняет буфер токенов до позиции N, если еще
не заполнен, возвращает N-1 токен из буфера
• next()

делает shift из lookup буфера, если он не пустой,
либо читает новый токен
Создается столько же токенов, 

но нужно меньше памяти в один
момент времени
45
Проблема:
заставляем GC плакать работать
46
Уменьшаем стоимость токенов:
«многоходовочка»
48
[
{
type: 'FullStop',
value: '.',
offset: 0,
line: 1,
column: 1
},
…
]
Строковые обозначения
удобны при отладке, но
они не выходят за рамки
сканера и можно
заменить на числа
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
},
…
]
Можно не хранить
подстроку – это особенно
расточительно для
одиночных символов;
к тому же многие многие
конструкции собираются
из нескольких токенов –
эффективнее брать одну
подстроку вместо
конкатенации нескольких
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
Да не просто Array, а TypedArray
Массив 

объектов
Массивы 

чисел
Array vs. TypedArray
• Не могут содержать дырок
• В теории быстрее (т.к. меньше проверок)
• Хранятся вне heap (если достаточно большие)
• Предзаполнены нулями
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
(кол-во токенов) 254 062 x 17 = 4.3Mb
4.3Mb vs. 12.7Mb(min)
57
Хьюстон, у нас проблемы:
TypedArray фиксированной длины,

а мы не знаем сколько токенов будет
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
(кол-во символов) 983 085 x 17 = 16.7Mb
16.7Mb vs. 12.7Mb (min)
60
16.7Mb vs. 12.7Mb (min)
60
Не повод сдаваться, 

давайте немного
подумаем…
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
Ок для коротких строк,
нужно кешировать для
длинных
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)
Меньше операций со строками
«Убийцы» производительности*
• RegExp
• Конкатенация строк
• toLowerCase/toUpperCase
• substr/substring
• …
69
* Засоряют GC и он все портит
«Убийцы» производительности*
• RegExp
• Конкатенация строк
• toLowerCase/toUpperCase
• substr/substring
• …
70
Без этого никак, 

но от остального
можно избавиться
* Засоряют GC и он все портит
71
var start = scanner.tokenStart;
…
scanner.next();
…
scanner.next();
…
return source.substr(start, scanner.tokenEnd);
Нет конкатенации!
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;
}
Сравнение строк
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;
}
Сравнение строк
Быстрое отсечение
по длине
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;
}
Сравнение строк
Сравниваем 

код за кодом
Как сравнивать 

без учета регистра*?
75
* То есть без toLowerCase/toUpperCase
Эвристика
• Сравниваем с заранее известными строками (str)
• Заранее заданные строки всегда в нижнем
регистре и содержат только латинские буквы
• Читал я как то в твиттере…
76
Чтобы перевести из верхнего регистра 

в нижний, нужно выставить 6-й бит в 1
(работает только для латинских букв)
'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;
}
}
…
}
Сравнение строк без учета регистра
Бенефиты
• Часто срабатывает быстрое отсечение
• Нет получения подстрок (не давим на GC)
• Нет получения временных строк 

(результат toLowerCase/toUpperCase)
• Операция сравнения не производит мусор
79
Результаты
• RegExp
• Конкатенация строк
• toLowerCase/toUpperCase
• substr/substring
80
Отказываемся от массивов
(от слова совсем)
Что не так с массивами
• Если растить массив, то происходит
копирование памяти + нагрузка на GC
• Мы не можем заранее знать размер массива
82
Решение?
83
Двусвязные списки
84
85
85
AST node AST node AST node AST node
Требует немного больше
памяти чем массивы, но…
86
Плюсы
• Не вызывает копирование памяти
• Не засоряет GC при построении AST
• Мы получаем next/prev
• Дешевая вставка/удаление
• Лучше для мономорфности
87
Всё это и многое другое позволило
уменьшить потребление памяти,
нагрузку на GC 

и ускорить вдвое
88
Но это еще не конец 😋
89
История ускорения #3
неделя после FrontTalks
Общие моменты
• Упрощение структуры AST
• Меньше потребление памяти, переиспользование
• list.map().join() -> цикл + конкатенация
• и по мелочи…
91
И снова про стоимость токенов
93
[
{
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 можно считать не всегда и лениво
94
95
[
{
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
Действительно ли для offsets
нужно 32 бита?
Эвристика: вряд ли кто-то будет парсить
CSS больше 16Mb
96
97
offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]
type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]
98
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]
+
=
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]
offsetAndType = [ 16777216, 788529157, … ]
+
=
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, … ]
start = offsetAndType[i] & 0xFFFFFF;
type = offsetAndType[i] >> 24;
+
=
101
[
{
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)
102
103
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;
}
}
Нужно всего 2 чтения для
3 значений, т.к. конец
становится началом
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;
}
}
Два чтения из массива – 

как то не круто…
105
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;
105
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 ]
Первое смещение
всегда ноль
107
offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]
type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]
Сдвигаем влево
108
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;
…
109
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;
}
}
Теперь можно в одно
чтение
110
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% чтений (~250k)
👌
Переиспользование
Сканер каждый раз создавал
новые массивы на каждый
разбор
112
Сканер каждый раз создавал
новые массивы на каждый
разбор
112
Новая стратегия
• По дефолту создается буфер в 16Kb
• Создается новый буфер, только если он мал
для разбираемого CSS
• Значительный прирост скорости, особенно в
сценариях разбора малых фрагментов CSS
113
114
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
Текущий результат
И это еще не конец… 😋
115
Минутка «рекламы»
CSSTree – 

не только про скорость
117
Новая фича*:
Разбор и матчинг синтаксиса
CSS значений
118
* Пока уникальная среди CSS парсеров
Пример
119
120
csstree.github.io/docs/syntax.html
Документация синтаксиса
121
csstree.github.io/docs/validator.html
Валидатор синтаксиса CSS значений
122
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);
}
});
Свой валидатор в 8 строк
Кое что еще
• csstree-validator – npm пакет + консольная команда
• stylelint-csstree-validator – плагин для stylelint
• gulp-csstree – плагин для gulp
• SublimeLinter-contrib-csstree – плагин для Sublime Text
• vscode-csstree – плагин для VS Code
• csstree-validator – плагин для Atom



More is coming…
123
Заключение
Хотите чтобы ваш JavaScript
работал так же быстро как Си,
сделайте его похожим на Си
125
Изучайте алгоритмы, структуры данных,
как работают JS-движки и GC – 

у вас будет больше вариантов для
оптимизаций
126
– К.О.
Доклады по теме
• CSSO – история ускорения

tinyurl.com/csso-speedup
• Парсим CSS

tinyurl.com/csstree-intro
127
github.com/csstree/csstree
128
Нужен ваш фидбек
Роман Дворнов
@rdvornov
github.com/lahmatiy
rdvornov@gmail.com
Вопросы?
github.com/csstree/csstree

Mais conteúdo relacionado

Mais procurados

Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...
Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...
Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...Ontico
 
Распределенные системы в Одноклассниках
Распределенные системы в ОдноклассникахРаспределенные системы в Одноклассниках
Распределенные системы в Одноклассникахodnoklassniki.ru
 
Java tricks for high-load server programming
Java tricks for high-load server programmingJava tricks for high-load server programming
Java tricks for high-load server programmingAndrei Pangin
 
Незаурядная Java как инструмент разработки высоконагруженного сервера
Незаурядная Java как инструмент разработки высоконагруженного сервераНезаурядная Java как инструмент разработки высоконагруженного сервера
Незаурядная Java как инструмент разработки высоконагруженного сервераodnoklassniki.ru
 
Caching data outside Java Heap and using Shared Memory in Java
Caching data outside Java Heap and using Shared Memory in JavaCaching data outside Java Heap and using Shared Memory in Java
Caching data outside Java Heap and using Shared Memory in JavaAndrei Pangin
 
Выжимаем из сервера максимум (Андрей Паньгин)
Выжимаем из сервера максимум (Андрей Паньгин)Выжимаем из сервера максимум (Андрей Паньгин)
Выжимаем из сервера максимум (Андрей Паньгин)Ontico
 
Использование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработкиИспользование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработкиvictor-yastrebov
 
Deep Dive C# by Sergey Teplyakov
Deep Dive  C# by Sergey TeplyakovDeep Dive  C# by Sergey Teplyakov
Deep Dive C# by Sergey TeplyakovAlex Tumanoff
 
Суперсилы Chrome DevTools — Роман Сальников, 2ГИС
Суперсилы Chrome DevTools — Роман Сальников, 2ГИССуперсилы Chrome DevTools — Роман Сальников, 2ГИС
Суперсилы Chrome DevTools — Роман Сальников, 2ГИСYandex
 
Очередной скучный доклад про логгирование
Очередной скучный доклад про логгированиеОчередной скучный доклад про логгирование
Очередной скучный доклад про логгированиеPython Meetup
 
Async clinic by by Sergey Teplyakov
Async clinic by by Sergey TeplyakovAsync clinic by by Sergey Teplyakov
Async clinic by by Sergey TeplyakovAlex Tumanoff
 
Производительность Unity3D: подводные камни / Алексей Чубарь (BIT.GAMES)
Производительность Unity3D: подводные камни / Алексей Чубарь (BIT.GAMES)Производительность Unity3D: подводные камни / Алексей Чубарь (BIT.GAMES)
Производительность Unity3D: подводные камни / Алексей Чубарь (BIT.GAMES)Ontico
 
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)Badoo Development
 
Haskell
HaskellHaskell
HaskellDevDay
 
Сверхоптимизация кода на Python
Сверхоптимизация кода на PythonСверхоптимизация кода на Python
Сверхоптимизация кода на Pythonru_Parallels
 
Семь тысяч Rps, один go
Семь тысяч Rps, один goСемь тысяч Rps, один go
Семь тысяч Rps, один goBadoo Development
 
2013 09 19 кеширование на клиенте и сервере
2013 09 19 кеширование на клиенте и сервере2013 09 19 кеширование на клиенте и сервере
2013 09 19 кеширование на клиенте и сервереYandex
 

Mais procurados (20)

Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...
Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...
Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...
 
Распределенные системы в Одноклассниках
Распределенные системы в ОдноклассникахРаспределенные системы в Одноклассниках
Распределенные системы в Одноклассниках
 
Java tricks for high-load server programming
Java tricks for high-load server programmingJava tricks for high-load server programming
Java tricks for high-load server programming
 
Незаурядная Java как инструмент разработки высоконагруженного сервера
Незаурядная Java как инструмент разработки высоконагруженного сервераНезаурядная Java как инструмент разработки высоконагруженного сервера
Незаурядная Java как инструмент разработки высоконагруженного сервера
 
Caching data outside Java Heap and using Shared Memory in Java
Caching data outside Java Heap and using Shared Memory in JavaCaching data outside Java Heap and using Shared Memory in Java
Caching data outside Java Heap and using Shared Memory in Java
 
Выжимаем из сервера максимум (Андрей Паньгин)
Выжимаем из сервера максимум (Андрей Паньгин)Выжимаем из сервера максимум (Андрей Паньгин)
Выжимаем из сервера максимум (Андрей Паньгин)
 
Использование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработкиИспользование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработки
 
Deep Dive C# by Sergey Teplyakov
Deep Dive  C# by Sergey TeplyakovDeep Dive  C# by Sergey Teplyakov
Deep Dive C# by Sergey Teplyakov
 
Суперсилы Chrome DevTools — Роман Сальников, 2ГИС
Суперсилы Chrome DevTools — Роман Сальников, 2ГИССуперсилы Chrome DevTools — Роман Сальников, 2ГИС
Суперсилы Chrome DevTools — Роман Сальников, 2ГИС
 
Очередной скучный доклад про логгирование
Очередной скучный доклад про логгированиеОчередной скучный доклад про логгирование
Очередной скучный доклад про логгирование
 
Java 8 puzzlers
Java 8 puzzlersJava 8 puzzlers
Java 8 puzzlers
 
Async clinic by by Sergey Teplyakov
Async clinic by by Sergey TeplyakovAsync clinic by by Sergey Teplyakov
Async clinic by by Sergey Teplyakov
 
Производительность Unity3D: подводные камни / Алексей Чубарь (BIT.GAMES)
Производительность Unity3D: подводные камни / Алексей Чубарь (BIT.GAMES)Производительность Unity3D: подводные камни / Алексей Чубарь (BIT.GAMES)
Производительность Unity3D: подводные камни / Алексей Чубарь (BIT.GAMES)
 
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
 
PowerShell
PowerShellPowerShell
PowerShell
 
Haskell
HaskellHaskell
Haskell
 
Component Inspector
Component InspectorComponent Inspector
Component Inspector
 
Сверхоптимизация кода на Python
Сверхоптимизация кода на PythonСверхоптимизация кода на Python
Сверхоптимизация кода на Python
 
Семь тысяч Rps, один go
Семь тысяч Rps, один goСемь тысяч Rps, один go
Семь тысяч Rps, один go
 
2013 09 19 кеширование на клиенте и сервере
2013 09 19 кеширование на клиенте и сервере2013 09 19 кеширование на клиенте и сервере
2013 09 19 кеширование на клиенте и сервере
 

Semelhante a Парсим CSS: performance tips & tricks

Статический анализ: вокруг Java за 60 минут
Статический анализ: вокруг Java за 60 минутСтатический анализ: вокруг Java за 60 минут
Статический анализ: вокруг Java за 60 минутAndrey Karpov
 
CSS глазами машин
CSS глазами машинCSS глазами машин
CSS глазами машинRoman Dvornov
 
CSSO — минимизируем CSS
 CSSO — минимизируем CSS CSSO — минимизируем CSS
CSSO — минимизируем CSSRoman Dvornov
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кодаAndrey Karpov
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода Pavel Tsukanov
 
Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioAndrey Karpov
 
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"Fwdays
 
Улучшение качества открытого программного обеспечения с помощью инструментов ...
Улучшение качества открытого программного обеспечения с помощью инструментов ...Улучшение качества открытого программного обеспечения с помощью инструментов ...
Улучшение качества открытого программного обеспечения с помощью инструментов ...Andrey Karpov
 
CSSO — сжимаем CSS Роман Дворнов, Avito
 CSSO — сжимаем CSS Роман Дворнов, Avito CSSO — сжимаем CSS Роман Дворнов, Avito
CSSO — сжимаем CSS Роман Дворнов, Avitoit-people
 
Язык программирования PascalABC.NET 2015. Новые возможности
Язык программирования PascalABC.NET 2015. Новые возможностиЯзык программирования PascalABC.NET 2015. Новые возможности
Язык программирования PascalABC.NET 2015. Новые возможностиМихалкович Станислав
 
Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Andrey Karpov
 
Всё о статическом анализе кода для Java программиста
Всё о статическом анализе кода для Java программистаВсё о статическом анализе кода для Java программиста
Всё о статическом анализе кода для Java программистаAndrey Karpov
 
200 open source проектов спустя: опыт статического анализа исходного кода
200 open source проектов спустя:опыт статического анализа исходного кода200 open source проектов спустя:опыт статического анализа исходного кода
200 open source проектов спустя: опыт статического анализа исходного кодаPositive Hack Days
 
200 open source проектов спустя: опыт статического анализа исходного кода
200 open source проектов спустя: опыт статического анализа исходного кода200 open source проектов спустя: опыт статического анализа исходного кода
200 open source проектов спустя: опыт статического анализа исходного кодаAndrey Karpov
 
Цена ошибки
Цена ошибкиЦена ошибки
Цена ошибкиAndrey Karpov
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3Eugeniy Tyumentcev
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3etyumentcev
 

Semelhante a Парсим CSS: performance tips & tricks (20)

Статический анализ: вокруг Java за 60 минут
Статический анализ: вокруг Java за 60 минутСтатический анализ: вокруг Java за 60 минут
Статический анализ: вокруг Java за 60 минут
 
CSS глазами машин
CSS глазами машинCSS глазами машин
CSS глазами машин
 
CSSO — минимизируем CSS
 CSSO — минимизируем CSS CSSO — минимизируем CSS
CSSO — минимизируем CSS
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кода
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода
 
Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-Studio
 
PascalABC.NET 2015-2016
PascalABC.NET 2015-2016PascalABC.NET 2015-2016
PascalABC.NET 2015-2016
 
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
 
Parallel STL
Parallel STLParallel STL
Parallel STL
 
Улучшение качества открытого программного обеспечения с помощью инструментов ...
Улучшение качества открытого программного обеспечения с помощью инструментов ...Улучшение качества открытого программного обеспечения с помощью инструментов ...
Улучшение качества открытого программного обеспечения с помощью инструментов ...
 
CSSO — сжимаем CSS Роман Дворнов, Avito
 CSSO — сжимаем CSS Роман Дворнов, Avito CSSO — сжимаем CSS Роман Дворнов, Avito
CSSO — сжимаем CSS Роман Дворнов, Avito
 
Язык программирования PascalABC.NET 2015. Новые возможности
Язык программирования PascalABC.NET 2015. Новые возможностиЯзык программирования PascalABC.NET 2015. Новые возможности
Язык программирования PascalABC.NET 2015. Новые возможности
 
Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?
 
Всё о статическом анализе кода для Java программиста
Всё о статическом анализе кода для Java программистаВсё о статическом анализе кода для Java программиста
Всё о статическом анализе кода для Java программиста
 
200 open source проектов спустя: опыт статического анализа исходного кода
200 open source проектов спустя:опыт статического анализа исходного кода200 open source проектов спустя:опыт статического анализа исходного кода
200 open source проектов спустя: опыт статического анализа исходного кода
 
200 open source проектов спустя: опыт статического анализа исходного кода
200 open source проектов спустя: опыт статического анализа исходного кода200 open source проектов спустя: опыт статического анализа исходного кода
200 open source проектов спустя: опыт статического анализа исходного кода
 
Цена ошибки
Цена ошибкиЦена ошибки
Цена ошибки
 
Цена ошибки
Цена ошибкиЦена ошибки
Цена ошибки
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3
 

Mais de Roman Dvornov

Unit-тестирование скриншотами: преодолеваем звуковой барьер
Unit-тестирование скриншотами: преодолеваем звуковой барьерUnit-тестирование скриншотами: преодолеваем звуковой барьер
Unit-тестирование скриншотами: преодолеваем звуковой барьерRoman Dvornov
 
Масштабируемая архитектура фронтенда
Масштабируемая архитектура фронтендаМасштабируемая архитектура фронтенда
Масштабируемая архитектура фронтенда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
 
Remote (dev)tools своими руками
Remote (dev)tools своими рукамиRemote (dev)tools своими руками
Remote (dev)tools своими рукамиRoman Dvornov
 
CSS parsing: performance tips & tricks
CSS parsing: performance tips & tricksCSS parsing: performance tips & tricks
CSS parsing: performance tips & tricksRoman Dvornov
 
CSSO – compress CSS (english version)
CSSO – compress CSS (english version)CSSO – compress CSS (english version)
CSSO – compress CSS (english version)Roman Dvornov
 
CSSO — сжимаем CSS (часть 2)
CSSO — сжимаем CSS (часть 2)CSSO — сжимаем CSS (часть 2)
CSSO — сжимаем CSS (часть 2)Roman Dvornov
 
Инструментируй это
Инструментируй этоИнструментируй это
Инструментируй этоRoman Dvornov
 
Жизнь в изоляции
Жизнь в изоляцииЖизнь в изоляции
Жизнь в изоляцииRoman Dvornov
 
Инструменты разные нужны, инструменты разные важны
Инструменты разные нужны, инструменты разные важныИнструменты разные нужны, инструменты разные важны
Инструменты разные нужны, инструменты разные важныRoman Dvornov
 
SPA инструменты
SPA инструментыSPA инструменты
SPA инструментыRoman Dvornov
 
Карточный домик
Карточный домикКарточный домик
Карточный домикRoman Dvornov
 
Баба Яга против!
Баба Яга против!Баба Яга против!
Баба Яга против!Roman Dvornov
 
Basis.js – «под капотом»
Basis.js – «под капотом»Basis.js – «под капотом»
Basis.js – «под капотом»Roman Dvornov
 
DOM-шаблонизаторы – не только "быстро"
DOM-шаблонизаторы – не только "быстро"DOM-шаблонизаторы – не только "быстро"
DOM-шаблонизаторы – не только "быстро"Roman Dvornov
 
Не бойся, это всего лишь данные... просто их много
Не бойся, это всего лишь данные... просто их многоНе бойся, это всего лишь данные... просто их много
Не бойся, это всего лишь данные... просто их многоRoman Dvornov
 
Быстро о быстром
Быстро о быстромБыстро о быстром
Быстро о быстромRoman Dvornov
 
Как построить DOM
Как построить DOMКак построить DOM
Как построить DOMRoman Dvornov
 
Компонентный подход: скучно, неинтересно, бесперспективно
Компонентный подход: скучно, неинтересно, бесперспективноКомпонентный подход: скучно, неинтересно, бесперспективно
Компонентный подход: скучно, неинтересно, бесперспективноRoman Dvornov
 

Mais de Roman Dvornov (20)

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

Парсим CSS: performance tips & tricks