Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

CSSO – история ускорения

1,223 views

Published on

CSSO – инструмент для минификации CSS, который не так давно вернулся к активной разработке. Помимо исправленных багов и новых фич, он значительно ускорился и стал одним из самых быстрых структурных минификаторов CSS.

Доклад о том как это достигалось, оптимизациях, деоптимизациях, структурах данных и подходах.

Holy.js, Санкт-Петербург, 5 июня 2016
Видео: https://www.youtube.com/watch?v=8o3gKKD_J4A

Published in: Technology
  • Be the first to comment

CSSO – история ускорения

  1. 1. CSSO — история ускорения Роман Дворнов Avito HolyJS Санкт-Петербург, 2016
  2. 2. Работаю в Avito Делаю SPA Автор basis.js Мейнтенер CSSO За любую движуху, 
 кроме голодовки ;)
  3. 3. ВремясжатияCSS(600Kb) 500 ms 1 000 ms 1 500 ms 2 000 ms 2 500 ms 3 000 ms 3 500 ms 4 000 ms 4 500 ms 5 000 ms 5 500 ms 6 000 ms Версия CSSO 1.4.0 1.5.0 1.6.0 1.7.0 1.8.0 2.0 1 050 ms clean-css Изменение скорости CSSO csso 500 ms cssnano 23 250 ms
  4. 4. Почему это важно?
  5. 5. 5 clean-css 3.4.16 cssnano 3.6.2 csso 2.1.1 ноябрь 2015 602 245 байт 430 240 2 039 ms 439 210 41 355 ms 435 629 714 ms апрель 2016 822 021 байт 587 906 2 546 ms 604 167 76 665 ms 595 834 793 ms июнь 2016 883 498 байт 632 007 2 588 ms 648 579 94 867 ms 639 495 803 ms Время минификации CSS Основной CSS файл ActiAgent.ru
  6. 6. Тренд изменения времени от размера CSSТрендизмененийразмераивремени 100 % 120 % 140 % 160 % 180 % 200 % 220 % 240 % Размер CSS 602 245 822 021 883 498 размер CSS clean-css cssnano csso 229% 146% 127% 112%
  7. 7. 7 Новые продвинутые оптимизации (usage data, rename, etc) 245 823 Время Размер (байт) 639 495 803 ms 2 513 ms Лучше сжатие, но требует 2-3х больше времени
  8. 8. 8 clean-css 3.4.16 cssnano 3.6.2 csso 2.1.1 15,5 sec 9 min 29 sec 4,8 sec Таких файлов в проекте несколько (6 больших) время минификации всех файлов
  9. 9. 8 clean-css 3.4.16 cssnano 3.6.2 csso 2.1.1 15,5 sec 9 min 29 sec 4,8 sec Таких файлов в проекте несколько (6 больших) время минификации всех файлов clean-css 3.4.16 cssnano 3.6.2 csso 2.1.1 не поддерживается (≈46,5 sec) не поддерживается (≈28 min 27 sec) ~15 sec + продвинутые оптимизации
  10. 10. 9 CSS сжатый CSS Процесс минификации
  11. 11. 9 CSS сжатый CSS Парсер Процесс минификации
  12. 12. 9 CSS сжатый CSS ASTПарсер Процесс минификации
  13. 13. 9 CSS сжатый CSS ASTПарсер Компрессор Процесс минификации
  14. 14. 9 CSS сжатый CSS AST AST Парсер Компрессор Процесс минификации
  15. 15. 9 CSS сжатый CSS AST AST Парсер Компрессор Транслятор Процесс минификации
  16. 16. 9 CSS сжатый CSS AST AST Парсер Компрессор Транслятор Процесс минификации Минификатор
  17. 17. Парсер
  18. 18. Основные шаги • Токенизация • Построение дерева (лексер) 11
  19. 19. Токенизация
  20. 20. 13 • whitespaces – [ nrtf]+ • keyword – [a-zA-aZ…]+ • number – [0-9]+ • string – "string" или 'string' • comment – /* comment */ • punctuation – [;,.#{}[]()…] Разбиение текста на токены
  21. 21. 14 .foo { width: 10px; } [ '.', 'foo', ' ', '{', 'n ', 'width', ':', ' ', '10', 'px', ';', 'n', '}' ]
  22. 22. Низкоуровневые штуки
  23. 23. 16 for (var i = 0; i < str.length; i++) { var ch = str.charAt(i); var chNext = str.charAt(i + 1); if (ch === '/' && chNext === '*') { result.push(readComment()); } else if (ch >= '0' && ch <= '9') { result.push(readNumber()); } ... } Ключевой цикл токенайзера
  24. 24. 17 for (var i = 0; i < str.length; i++) { var ch = str.charAt(i); var chNext = str.charAt(i + 1); if (ch === '/' && chNext === '*') { result.push(readComment()); } else if (ch >= '0' && ch <= '9') { result.push(readNumber()); } ... } Ключевой цикл токенайзера
  25. 25. 18 for (var i = 0; i < str.length; i++) { var ch = str.charAt(i); var chNext = str.charAt(i + 1); if (ch === '/' && chNext === '*') { result.push(readComment()); } else if (ch >= '0' && ch <= '9') { result.push(readNumber()); } ... } Ключевой цикл токенайзера
  26. 26. 19 for (var i = 0; i < str.length; i++) { var ch = str.charAt(i); var chNext = str.charAt(i + 1); if (ch === '/' && chNext === '*') { result.push(readComment()); } else if (ch >= '0' && ch <= '9') { result.push(readNumber()); } ... } Ключевой цикл токенайзера
  27. 27. 20 for (var i = 0; i < str.length; i++) { var ch = str.charAt(i); var chNext = str.charAt(i + 1); if (ch === '/' && chNext === '*') { result.push(readComment()); } else if (ch >= '0' && ch <= '9') { result.push(readNumber()); } ... } Ключевой цикл токенайзера
  28. 28. Обычный код – простор для потери быстродействия 21
  29. 29. Строки vs. числа
  30. 30. 23 for (var i = 0; i < str.length; i++) { var ch = str.charAt(i); var chNext = str.charAt(i + 1); if (ch === '/' && chNext === '*') { result.push(readComment()); } else if (ch >= '0' && ch <= '9') { result.push(readNumber()); } ... } Сравнение символов
  31. 31. 24 • Строка – последовательность чисел • Извлечение символа = получение новой строки • Сравнение символов ≈ сравнение строк Нужно помнить
  32. 32. 25 !!!
  33. 33. 26 // выносим коды символов в константы var STAR = 42; var SLASH = 47; var ZERO = 48; var NINE = 57; ... Делаем быстрее
  34. 34. 27 for (var i = 0; i < str.length; i++) { var code = str.charCodeAt(i); var codeNext = str.charCodeAt(i + 1); if (code === SLASH && codeNext === STAR) { result.push(readComment()); } else if (code >= ZERO && code <= NINE) { result.push(readNumber()); } ... } charCodeAt() + числовые константы
  35. 35. 28 ✔ Нет больше StringCharFromCode ✔ Всегда сравнение чисел
  36. 36. 28 ✔ Нет больше StringCharFromCode ✔ Всегда сравнение чисел ??? Загрузка значения перед сравнением
  37. 37. Используй const, Люк! 29 var STAR = 42; var SLASH = 47; var ZERO = 48; var NINE = 57; ... const STAR = 42; const SLASH = 47; const ZERO = 48; const NINE = 57; ...
  38. 38. 30 WTF?
  39. 39. – Вячеслав Егоров “... [const] это все-таки неизменяемая привязка переменной к значению ... С другой стороны виртуальная машина может и должна бы использовать это самое свойство неизменяемости ... V8 не использует, к сожалению.” 31 habrahabr.ru/company/jugru/blog/301040/#comment_9622474
  40. 40. 32 for (var i = 0; i < str.length; i++) { var code = str.charCodeAt(i); var codeNext = str.charCodeAt(i + 1); if (code === 47 && codeNext === 42) { result.push(readComment()); } else if (code >= 48 && code <= 57) { result.push(readNumber()); } ... } Но мы люди упоротые упорные
  41. 41. 32 for (var i = 0; i < str.length; i++) { var code = str.charCodeAt(i); var codeNext = str.charCodeAt(i + 1); if (code === 47 && codeNext === 42) { result.push(readComment()); } else if (code >= 48 && code <= 57) { result.push(readNumber()); } ... } Но мы люди упоротые упорные Положительный эффект не замечен…
  42. 42. 33 356ms – chatAt() 307ms – charCodeAt() Результаты на ~14% быстрее
  43. 43. Осторожней со строками
  44. 44. 35 for (var i = 0; i < str.length; i++) { var code = str.charCodeAt(i); var codeNext = str.charCodeAt(i + 1); if (code === SLASH && codeNext === STAR) { result.push(readComment()); } else if (code >= ZERO && code <= NINE) { result.push(readNumber()); } ... } Избегаем лишних чтений из строки
  45. 45. 36 for (var i = 0; i < str.length; i++) { var code = str.charCodeAt(i); if (code === SLASH) { var codeNext = str.charCodeAt(i + 1); if (codeNext === STAR) { result.push(readComment()); continue; } } ... } Избегаем лишних чтений из строки 307ms → 292 ms (-5%)
  46. 46. 37 for (var i = 0; i < str.length; i++) { var code = str.charCodeAt(i); if (code === SLASH) { var codeNext = str.charCodeAt(i + 1); if (codeNext === STAR) { result.push(readComment()); continue; } } ... } Опасная операция!
  47. 47. 38 Попытка чтения за пределами 
 строки или массива 
 приводит к деоптимизации
  48. 48. 39 var codeNext = 0; if (i + 1 < str.length) { codeNext = str.charCodeAt(i + 1); } Избегаем выхода за пределы строки 292ms → 69 ms (в 4 раза быстрее!)
  49. 49. Меньше сравнений
  50. 50. 41 if (code >= ZERO && code <= NINE) { // число } else if (code === SINGLE_QUOTE || code === DOUBLE_QUOTE) { // строка } else if (code === SPACE || code === N || code === R || code === F) { // whitespace } … Много сравнений
  51. 51. 42 var PUNCTUATION = { 9: 'Tab', // 't' 10: 'Newline', // 'n' ... 126: 'Tilde' // '~' }; ... } else if (code in PUNCTUATION) { // символ пунктуации } ... Проверка в словаре – очень дорого
  52. 52. 43 var CATEGORY_LENGTH = Math.max.apply(null, Object.keys(PUNCTUATION)) + 1; var CATEGORY = new Uint32Array(CATEGORY_LENGTH); Object.keys(PUNCTUATION).forEach(function(key) { CATEGORY[key] = PUNCTUATOR; }); for (var i = ZERO; i <= NINE; i++) { CATEGORY[i] = DIGIT; } CATEGORY[SINGLE_QUOTE] = STRING; CATEGORY[DOUBLE_QUOTE] = STRING; CATEGORY[SPACE] = WHITESPACE; ... Создаем карту категорий
  53. 53. 44 switch (code < CATEGORY_LENGTH ? CATEGORY[code] : 0) { case DIGIT: // число case STRING: // строка case WHITESPACE: // whitespace case PUNCTUATOR: // символ пунктуации ... } Уменьшаем число сравнений 69ms → 14 ms (еще в 5 раз быстрее!)
  54. 54. Предварительные итоги
  55. 55. Результат: 356ms → 14 ms Цифры получены на упрощенном примере, на практике всё сложнее Примеры тестов: gist.github.com/lahmatiy/e124293c2f2b98e847d0f2bb54c2738e 46
  56. 56. 47 • charAt() → charCodeAt() • Избегайте лишних операций со строками • Контролируйте выход за пределы • Уменьшайте количество сравнений Подводим итоги
  57. 57. Токен – не только строка
  58. 58. Нужна дополнительная информация о токене: тип и локация 49
  59. 59. 50 .foo { width: 10px; } [ { type: 'FullStop', value: '.', offset: 0, line: 1, column: 1 }, … ]
  60. 60. Как хранить? 51 Object Array Понятнее Компактнее { type: 'FullStop', value: '.', offset: 1, line: 2, column: 3 } ['FullStop', '.', 1, 2, 3]
  61. 61. Размер токена 52 Object Array 64 байт 88 байт
  62. 62. Размер токена 52 Object Array 64 байт 88 байт Наш CSS – 885Kb 256 163 токена 16,4 Мб 22,5 Мб разница 6,1 Мб
  63. 63. Что быстрее? 53 Object Array Время создания (литералов) сопоставимо,
 меняется в зависимости от задачи Обработка объектов (чтение) гораздо быстрее Нет простого ответа gist.github.com/lahmatiy/90fa91d5ea89465d08c7db1e591e91c2
  64. 64. Меняем подход
  65. 65. Последовательные токенайзер и лексер – проще, но ведет к лишним затратам памяти и медленней 55
  66. 66. Scanner (ленивый токенайзер) 56
  67. 67. 57 scanner.token // текущий токен или null scanner.next() // переход к следующему токену scanner.lookup(N) // заглядывание вперед, возвращает // токен на N-ой позиции от текущей Основное API
  68. 68. 58 • lookup(N)
 заполняет буфер токенов до позиции N, если еще не заполнен, возвращает N-1 токен из буфера • next()
 делает shift из lookup буфера, если он не пустой, либо читает новый токен
  69. 69. Создается столько же токенов, 
 но нужно меньше памяти в один момент времени 59
  70. 70. 60 • Переиспользование объектов токенов • Пул токенов фиксированного размера Неудачные эксперименты Выигрыш практически не ощущается, но код гораздо сложнее и в разных случаях нужен пул разного размера, иногда очень большой
  71. 71. Сборка дерева
  72. 72. 62 • Избавление от look ahead • Array → Object • Array → List • CST → AST • … Как ускорялся
  73. 73. Look ahead
  74. 74. 64 function getSelector() { var node = ['Selector']; while (i < tokens.length) { if (checkId()) { node.push(getId()); } else if (checkClass()) { node.push(getClass()); } else ... } return node; } Было
  75. 75. 65 function getSelector() { var node = ['Selector']; while (i < tokens.length) { if (checkId()) { node.push(getId()); } else if (checkClass()) { node.push(getClass()); } else ... } return node; } Было Checker-функции – проверяют, подходящая ли последовательность токенов чтобы собрать AST узел
  76. 76. 66 function getSelector() { var node = ['Selector']; while (i < tokens.length) { if (checkId()) { node.push(getId()); } else if (checkClass()) { node.push(getClass()); } else ... } return node; } Было Build-функции – собирают AST узел, 
 у них есть гарантия, 
 что впереди подходящая последовательность токенов
  77. 77. 67 • Многократный проход по списку токенов • Нужны все токены сразу • Большое дублирование кода (проверок) • Сложно определить местоположение, в случае синтаксической ошибки в CSS Проблемы
  78. 78. 68 StyleSheet Ruleset Selector SimpleSelector Id | Class | Attribute … … Block … … Насколько все плохо checkStyleSheet() проверяла всю последовательность токенов, прежде чем начиналась сборка AST
  79. 79. – Cascading Style Sheets Level 2 Specification “The lexical scanner for the CSS core syntax in section 4.1.1 can be implemented 
 as a scanner without back-up.” 69 www.w3.org/TR/CSS22/grammar.html#q4
  80. 80. 70 while (scanner.token !== null) { switch (scanner.token.type) { case TokenType.Hash: // # node.push(getId()); break; case TokenType.FullStop: // . node.push(getClass()); break; … } Стало Быстрая проверка типа токена. В большинстве случае этого достаточно, чтобы выбрать подходящую функцию сборки
  81. 81. 71 function getPseudo() { var next = scanner.lookup(1); if (next.type === TokenType.Colon) { return getPseudoElement(); } return getPseudoClass(); } Стало Заглядывать вперед нужно редко, в этих случаях используется scanner.lookup(N)
  82. 82. 72 if (scanner.token.type !== TokenType.Something) { throwError('Something wrong here'); } Стало Если встречается что-то "не то" – 
 тут же выбрасывается сообщение об ошибке
  83. 83. 73 • Значительно быстрее • Используется ленивый токенайзер • Код проще, мало дублирования • Точное местоположение для ошибок в CSS Сплошные плюсы
  84. 84. Array → Object
  85. 85. 75 [ "stylesheet", [ "atrules", [ "atkeyword", [ "ident", "import" ] ], [ "s", " " ], [ "string", "'path/to/file.css'" ] ] ] Было – массив массивов
  86. 86. 76 { "type": "StyleSheet", "rules": [{ "type": "Atrule", "name": "import", "expression": { "type": "AtruleExpression", "sequence": [ ... ] }, "block": null }] } Стало
  87. 87. 77 • Требуется меньше памяти • Быстрее работа с узлами • Проще писать/читать код 
 (node[1] vs. node.value) Результат
  88. 88. 78 Помни – меньше мутаций! function createNode() { var node = { type: 'Type' }; if (expression1) { node.foo = 123; } if (expression2) { node.bar = true; } return node; } При добавлении нового свойства создается новый hidden class (здесь до 4). Узел одного типа, 
 но объекты разных классов – принимающие функции станут полиморфными.
  89. 89. 79 Добавляется новое свойство → меняется hidden class
  90. 90. 80 Смена hidden class
  91. 91. 81 Фиксим function createNode() { var node = { type: 'Type', foo: null, bar: false }; if (expression1) { node.foo = 123; } if (expression2) { node.bar = true; } return node; } При создании объекта стоит указывать все возможные свойства – будет один hidden class
  92. 92. 82 Все стало проще И функции работающие с объектами 
 только этого типа будут мономорфными
  93. 93. CST → AST
  94. 94. 84 • AST (Abstract Syntax Tree)
 логическая структура • CST (Concrete Syntax Tree)
 логическая структура + информация о форматировании AST vs. CST
  95. 95. 85 • AST – не нужно форматирование
 linters, minifiers, beautifiers, … • CST – форматирование важно
 linters, autoprefixer, postcss и друзья AST vs. CST
  96. 96. Раньше парсер возвращал CST, то есть сохранял все 
 пробелы, комментарии и разделители 86
  97. 97. 87 .foo > .bar, .baz { border: 1px solid red; // I'm useless comment } Отмеченное желтым – не нужно, 
 но сохранялось в дереве, а компрессор это все удалял
  98. 98. Так же теперь парсер не собирает заведомо неверные конструкции, 
 что упрощает и ускоряет дальнейшую работу с деревом 
 (т.к. не требуются лишние проверки) 88
  99. 99. 89 { "type": "Dimension", "value": { "type": "Number", "value": "123" }, "unit": { "type": "Identifier", "name": "px" } } Упрощен формат некоторых узлов { "type": "Dimension", "value": "123", "unit": "px" }
  100. 100. 90 • Меньше узлов в дереве • Не нужен обход, чтобы удалить ненужное • Многое проще не добавлять в дерево, чем потом искать и удалять Результат
  101. 101. 91 • Наш CSS – 885Kb • было узлов – 281 750 • стало узлов – 139 858 • Экономия памяти ~7Mb • В два раза быстрее обход В цифрах
  102. 102. Подводим итоги
  103. 103. 93 • В ~5 раз быстрее • В ~3 раза меньше потребление памяти • Проще формат • Дешевле обход и обработка AST Улучшения парсера
  104. 104. Компрессор
  105. 105. 95 • Однопроходная реструктуризация • Уменьшение числа обходов • Уменьшение глубины обхода • Минимум вычислений, мутаций узлов, копирования • Учет специфики CSS • … Как ускорялся
  106. 106. Однопроходная реструктуризация
  107. 107. 97 var prevAst = ast; var prev = translate(ast); while (true) { var resAst = restruct(copy(prevAst)); var res = translate(resAst); if (res.length > prev.length) { resAst = prevAst; break; } prevAst = resAst; prev = res; } Трансформируем пока есть положительный эффект
  108. 108. 98 var prevAst = ast; var prev = translate(ast); while (true) { var resAst = restruct(copy(prevAst)); var res = translate(resAst); if (res.length > prev.length) { resAst = prevAst; break; } prevAst = resAst; prev = res; } На каждой итерации • копирование дерева
  109. 109. 99 var prevAst = ast; var prev = translate(ast); while (true) { var resAst = restruct(copy(prevAst)); var res = translate(resAst); if (res.length > prev.length) { resAst = prevAst; break; } prevAst = resAst; prev = res; } На каждой итерации • копирование дерева • трансляция в строку
  110. 110. 100 var prevAst = ast; var prev = translate(ast); while (true) { var resAst = restruct(copy(prevAst)); var res = translate(resAst); if (res.length > prev.length) { resAst = prevAst; break; } prevAst = resAst; prev = res; } На каждой итерации • копирование дерева • трансляция в строку • бай-бай GC
  111. 111. 101 var prevAst = ast; var prev = translate(ast); while (true) { var resAst = restruct(copy(prevAst)); var res = translate(resAst); if (res.length > prev.length) { resAst = prevAst; break; } prevAst = resAst; prev = res; } На каждой итерации • копирование дерева • трансляция в строку • бай-бай GC 😱
  112. 112. Стало 102 restruct(ast); • Не ошибка – один проход • Нет копирования AST, трансляции всего дерева в строку • Пришлось поменять алгоритмы • Меньше нагрузка на GC, быстрее в 3-4 раза
  113. 113. Глубина обхода
  114. 114. 104 Наш CSS – 885Kb 139 858 узлов Холостой обход дерева 31 ms
  115. 115. Многим трансформациям необходимо обходить лишь Ruleset и AtRule 105
  116. 116. 106 • walkAll() – обход всех узлов • walkRules() – обход только правил и at-rules • walkRulesRight() – обход только правил 
 и at-rules в обратном порядке Walker под задачу
  117. 117. 107 Наш CSS – 139 858 узлов walkAll() 31 ms Холостой обход дерева walkRules(), walkRulesRight() 2 ms обходится 6 154 узла (4%)
  118. 118. Меньше обходов
  119. 119. 109 walkAll(ast, function(node) { if (node.type === 'Number') { … } }); walkAll(ast, function(node) { if (node.type === 'String') { … } }); … Было Для каждого типа узла отдельный обход Как мы помним, для нашего дерева операция стоит 31 ms
  120. 120. 110 walkAll(ast, function(node) { if (node.type === 'Number') { … } }); walkAll(ast, function(node) { if (node.type === 'String') { … } }); … Было Функции получают все узлы дерева, а они разных hidden class. Так функции будут полиморфными, хотя работают (обычно) только с одним типом узлов
  121. 121. 111 var handlers = { Number: packNumber, String: packString, … }; walkAll(ast, function(node, item, list) { if (handlers.hasOwnProperty(node.type)) { handlers[node.type].call(this, node, item, list); } }); Стало Один проход
  122. 122. 112 var handlers = { Number: packNumber, String: packString, … }; walkAll(ast, function(node, item, list) { if (handlers.hasOwnProperty(node.type)) { handlers[node.type].call(this, node, item, list); } }); Стало Единственная полиморфная функция, но простая
  123. 123. 113 var handlers = { Number: packNumber, String: packString, … }; walkAll(ast, function(node, item, list) { if (handlers.hasOwnProperty(node.type)) { handlers[node.type].call(this, node, item, list); } }); Стало Функции оказываются мономорфными (им приходят объекты одного hidden class)
  124. 124. 114 var handlers = { Number: packNumber, String: packString, … }; walkAll(ast, function(node, item, list) { if (handlers.hasOwnProperty(node.type)) { handlers[node.type].call(this, node, item, list); } }); Стало По результатам экспериментов, наиболее быстрый вариант решения
  125. 125. Общий итог
  126. 126. 116 • В 10+ раз быстрее • В 7+ раз меньше потребление памяти • В настоящий момент быстрее и эффективнее 
 по ресурсам других структурных минификаторов 
 (cssnano, clean-css) Текущие результаты goalsmashers.github.io/css-minification-benchmark/
  127. 127. Работы не закончены, можно сделать еще лучше ;) 117
  128. 128. CSSO — сжимаем CSS 118 www.slideshare.net/basisjs/csso-css-2 Немного о принципах минификации, 
 новых продвинутых оптимизациях и открытых вопросах
  129. 129. Бонус трек
  130. 130. Обкладывайте операции замерами 120 > echo '.test { color: green; }' | csso --debug ## parsing done in 11 ms Compress block #1 [0.000s] init [0.001s] clean [0.001s] compress [0.004s] prepare [0.001s] initialMergeRuleset [0.000s] mergeAtrule [0.000s] disjoinRuleset [0.001s] restructShorthand [0.003s] restructBlock ...
  131. 131. Hi-res timing 121 var startTime = process.hrtime(); // your code var elapsed = process.hrtime(startTime); console.log(parseInt(elapsed[0] * 1e3 + elapsed[1] / 1e6)); var startTime = performance.now(); // your code console.log(performance.now() - startTime); Node.js Browser
  132. 132. Memory usage 122 var mem = process.memoryUsage().heapUsed; // your code console.log(process.memoryUsage().heapUsed - mem); Очень грубый метод, лучшего простого способа пока нет :'(
  133. 133. Привычные инструменты
  134. 134. devtool 124 Runs Node.js programs inside Chrome DevTools npm install -g devtool github.com/Jam3/devtool
  135. 135. > devtool your-script.js 125
  136. 136. Заглядывайте под капот
  137. 137. 127 node --trace-hydrogen --trace-phase=Z --trace-deopt --code-comments --hydrogen-track-positions --redirect-code-traces --redirect-code-traces-to=code.asm --trace_hydrogen_file=code.cfg --print-opt-code your-script.js Получаем данные о работе кода
  138. 138. 128 mrale.ph/irhydra/2/ code.asm code.cfg
  139. 139. Самое сложное научиться это понимать ;) 130
  140. 140. Заключение
  141. 141. Вам это все не нужно, но однажды может пригодиться 132
  142. 142. 133 • Подбирать решение под задачу • Меньше тратить памяти • Меньше мутаций • Проще структуры • Разбираться как выполняется код • Перестать думать, что вы умней VM и наоборот ;) Капитанские советы
  143. 143. Нет серебряной пули – экспериментируйте, заменяйте, 
 смотрите под капот 134
  144. 144. Изучайте алгоритмы и структуры данных – правильный их выбор может дать больше чем оптимизация кода 135
  145. 145. Роман Дворнов @rdvornov rdvornov@gmail.com Вопросы? github.com/css/csso

×