Pro Yearly is on sale from $80 to $50! »

swift javascript

swift javascript

如何正当的令JS 运行如飞?!

6002ee051e03f0b762642ee7fafd111f?s=128

Zoom.Quiet

May 06, 2013
Tweet

Transcript

  1. JavaScript 极速快感 @ariyahidayat 旧金山, 2013年4月11日 1

  2. 成员 2

  3. 高性能原则 3

  4. 高性能的秘密 避免 低效 代码 使用 高速 代码 4

  5. 计算机编程艺术, Knuth’s Turing Award lecture (1974) 实际的问题是程序员在不恰当的时机费了大 量的精力来关注程序的效率。 过早优化是万恶之源 (或者至少是适用于大多数的编程过程)

    5
  6. “ 大岩石 ” “优先处理重要的部分 ”, Steven Covey 6

  7. 历程:象限 http://ariya.ofilabs.com/2012/11/optimization-journey-vs-destination.html 7 性能 可维护性

  8. 高级选项 8

  9. JavaScript 引擎 SpiderMonkey Firefox JavaScriptCore/Nitro Safari V8 Node.js, Chrome JScript/Chakra

    Internet Explorer Carakan Opera 9
  10. 构建组成元素 源码 语法树 内部对象, 宿主对象, ... 10 虚拟机/ 解释器 解析器

    运行时
  11. var answer = 42 解析器 关键字 等号 标识 数字 变量声明

    标识 字面常量 标记化 → Tokens 语法分析 → 语法树 11
  12. 语法视图 http://esprima.org/demo/parse.html 12

  13. 在解释器中运行 语法树访问者 遍历语法树 运行每个语法节点 二进制代码解释器 遍历并生成 二进制代码 在虚拟机里执行二进制代码 JIT 编译

    生成机器码 转换控制代码 13
  14. 执行步骤 http://toolness.github.io/slowmo-js/ 14

  15. 运行视图 http://int3.github.io/metajs/ 15

  16. 运行效率等级 基础的 优化的 快速启动 最小化内存占用 正常的执行速度 密集初始化 可能更多的内存需求 非常快的执行速度 button.onclick

    = function() { doSomething(); } for (i = 0; i < employees.length; ++i) total += employees[i].salary; 16
  17. 使用 V8 17

  18. 使用 V8 git clone git://github.com/v8/v8.git cd v8 make dependencies make

    x64.release -j4 make x64.debug -j4 18
  19. 性能分析 19

  20. http://ariya.ofilabs.com/2012/12/javascript-performance-analysis-sampling-tracing-and-timing.html 计时 准备计时工具 记录程序开始和结束时间 取样 周期性地检查正在执行的函数 追踪 追踪所有函数调用和退出 20

  21. 执行时间度量 http://calendar.perfplanet.com/2010/bulletproof-javascript-benchmarks/ var start = Date.now(); for (var i =

    0; i < 100; i++) { doSomething(); } console.log(Date.now() - start, 'ms'); 21
  22. 置信度度量 准确度 精密度 22

  23. 使用 Benchmark.js var suite = new Benchmark.Suite; suite.add('String#indexOf', function() {

    'Hello World!'.indexOf('o') > -1; }) .on('complete', function() { console.log('Fastest is ' + this.filter('fastest').pluck('name')); }) .run(); http://benchmarkjs.com/ 23
  24. 循环中常量的移动 你写的代码 JS引擎优化后的代码 for (var i = 0; i <

    100; i++) { sum += Math.sqrt(2) * i; } var temp = Math.sqrt(2); for (var i = 0; i < 100; i++) { sum += temp * i; } 代码偏移 → 精确度损失 24
  25. 删除无用代码 http://blogs.msdn.com/b/ie/archive/2010/11/17/html5-and-real-world-site- performance-seventh-ie9-platform-preview-available-for-developers.aspx 你写的代码 JS引擎优化后的代码 function test() { var a

    = 0, b = 0; for (var i = 0; i < 100; i++) { a += i; b += i * i; } return a; } function test() { var a = 0; for (var i = 0; i < 100; i++) { a += i; } return a; } 25
  26. 运行时优化 26

  27. 哪些需要 (不需要)优化 方法调用 垃圾回收 固定对象结构 特征分析 27

  28. 方法调用 28

  29. “ 方法调用很耗资源 ” sortDepartment totalDepartementExpense totalEmployeesSalaries getEmployeeData 29

  30. 自动内联 你写代码 js引擎优化后的代码 function square(x) { return x * x;

    } function f(x) { var sum = 0; for (var i = 0; i < x; i++) { sum += square(i); } return sum; } function f(x) { var sum = 0; for (var i = 0; i < x; i++) { sum += i * i; } return sum; } http://ariya.ofilabs.com/2013/04/automatic-inlining-in-javascript-engines.html 30
  31. V8 内联跟踪 http://floitsch.blogspot.com/2012/03/optimizing-for-v8-inlining.html d8 --trace-inlining example.js Did not inline InstantiateFunction

    called from Instantiate (target text too big). Did not inline Instantiate called from Instantiate (target not inlineable). Did not inline called from Instantiate (target not inlineable). Did not inline ConfigureTemplateInstance called from Instantiate (target not inlineable). Did not inline ToObject called from valueOf (target not inlineable). Did not inline FunctionSourceString called from toString (target not inlineable). Did not inline InstantiateFunction called from Instantiate (target text too big). Did not inline Instantiate called from Instantiate (target not inlineable). Did not inline ConfigureTemplateInstance called from Instantiate (target not inlineable). Inlined square called from f. Inlined square called from f. 31 译者说明:本人编译的 V8 (version 3.17.15.4) 运行该条命令后并没 有得到上面的结果。 使用命令: d8 --trace-inlining --stress-opt example.js 在有压力测试的 时候得到如右图的结果。 V8 的 debug 版本支持命令: d8 --trace_ic example.js 可以输出更为 详细的内联代码说明。
  32. 延迟解析 为了进一步减少首次执行指令的时间, Chakra 使用了延迟解析的技术,只在当函数即将执 行时才解析并生成二进制码 http://blogs.msdn.com/b/ie/archive/2012/06/13/advances-in-javascript- performance-in-ie10-and-windows-8.aspx 32

  33. 源码, 语法树, 内存 http://ariya.ofilabs.com/2012/07/lazy-parsing-in-javascript-engines.html function add(x, y) { return x

    + y; } function mul(x, y) { return x * y; } alert(add(40, 2)); { "type": "Program", "body": [ { "type": "FunctionDeclaration", "id": { "type": "Identifier", "name": "add" }, "params": [ { "type": "Identifier", "name": "x" }, { "type": "Identifier", "name": "y" } ], "defaults": [], "body": { "type": "BlockStatement", "body": [ { "type": "ReturnStatement", "argument": { "type": "BinaryExpression", "operator": "+", "left": { "type": "Identifier", "name": "x" }, "right": { "type": "Identifier", "name": "y" } } } ] }, "rest": null, "generator": false, "expression": false }, { "type": "FunctionDeclaration", "id": { "type": "Identifier", "name": "mul" }, "params": [ { "type": "Identifier", "name": "x" }, { "type": "Identifier", "name": "y" } ], "defaults": [], "body": { "type": "BlockStatement", "body": [ { "type": "ReturnStatement", "argument": { "type": "BinaryExpression", "operator": "*", "left": { "type": "Identifier", "name": "x" }, "right": { "type": "Identifier", "name": "y" } } } ] }, "rest": null, "generator": false, "expression": false }, { "type": "ExpressionStatement", "expression": { "type": "CallExpression", "callee": { "type": "Identifier", "name": "alert" }, "arguments": [ { "type": "CallExpression", "callee": { "type": "Identifier", "name": "add" }, "arguments": [ { "type": "Literal", "value": 40, "raw": "40" }, { "type": "Literal", "value": 2, "raw": "2" } ] } ] } } ] } { "type": "FunctionDeclaration", "id": { "type": "Identifier", "name": "add" }, "params": [ { "type": "Identifier", "name": "x" }, { "type": "Identifier", "name": "y" } ] }; 33
  34. 深入理解 function add(x, y) { return x + y; }

    声明一个 add 方法. 接受参数 x 和 y. 包含一条有返回值的语句. 返回的是 x + y 二元运算后的结果. function mul(x, y) { return x * y; } 声明一个 mul 方法. 接受参数 x 和 y. 包含一条有返回值的语句. 返回的是 x × y 二元运算后的结果. alert(add(40, 2)); 调用 alert 方法. alert参数是 add方法 以40 和 2 为参数执行后的 返回值. 34
  35. 惰性加载 function add(x, y) { return x + y; }

    声明一个 add 方法,包含的方法体是 “{ return x + y; }”. function mul(x, y) { return x * y; } 声明一个 mul 方法,包含的方法体是 “{ return x * y; }”. alert(add(40, 2)); 调用 alert 方法. alert参数是 add方法 以40 和 2 为参数执行后的返 回值. 调用add方法,此时 add 还没有被解析. 解析方法体 “{ return x + y; }”. 接受参数 x 和 y.返回的是 x + y 二元运算后的结果. 35
  36. 固定对象结构 36

  37. 对象结构变化 function Vehicle() { this.color = 'white'; this.speed = 0;

    } var car = new Vehicle(); car.color = 'black'; color speed var motorbike = new Vehicle(); motorbike.wheels = ['front', 'back']; car.maker = 'BMW'; color speed maker color speed wheels 37
  38. 属性访问 console.log(car.color); 检查对象是否包含 ‘color’ 属性. 如果没有找到,在原型链上向上查找. 获取属性值. 问题: 如果 color

    不存在 会怎样? 38
  39. 加快属性访问 执行通用属性访问 color speed 结构 检查 从属性中获取值 #1 console.log(car.color); 39

  40. 固定结构 http://ariya.ofilabs.com/2012/02/javascript-object-structure-speed-matters.html var universe = { answer: 42 }; //

    do something else universe.panic = false; var universe = { answer: 42, panic: true }; // do something else universe.panic = false; 提示: 单一的内联缓存减少影响 40
  41. 避免出现条件性的对象结构变化 var vehicle = { color: 'blue' }; if (currentYear

    > 2014) vehicle.backupCamera = new Camera(); var vehicle = { color: 'blue', backupCamera: null }; if (currentYear > 2014) vehicle.backupCamera = new Camera(); 41
  42. 垃圾回收 42

  43. 使用最小化的对象结构 http://www.youtube.com/watch?v=Vo72W1HWeFI var tick = new Date(); for (var i

    = 0, j = 0; i < 4e4; ++i) { var delta = new Date(); delta = delta - tick; tick = new Date(); j += delta; } var tick = Date.now(); for (var i = 0, j = 0; i < 4e4; ++i) { var delta = Date.now() - tick; tick = Date.now(); j += delta; } 大量 小的 Date 对象 43
  44. 追踪V8的垃圾回收 d8 --trace-gc example.js new Date() Date.now() 56 ms: Scavenge

    1.8 (36.0) -> 0.9 (36.0) MB, 2.8 ms 73 ms: Scavenge 1.8 (36.0) -> 0.9 (37.0) MB, 2.8 ms 89 ms: Scavenge 1.9 (37.0) -> 0.9 (37.0) MB, 1.0 ms 109 ms: Scavenge 1.9 (37.0) -> 0.9 (37.0) MB, 1.0 ms 126 ms: Scavenge 1.9 (37.0) -> 0.9 (37.0) MB, 0.9 ms 141 ms: Scavenge 1.9 (37.0) -> 0.9 (37.0) MB, 0.9 ms 159 ms: Scavenge 1.9 (37.0) -> 0.9 (37.0) MB, 0.9 ms 176 ms: Scavenge 1.9 (37.0) -> 0.9 (37.0) MB, 1.0 ms 192 ms: Scavenge 1.9 (37.0) -> 0.9 (37.0) MB, 0.9 ms 207 ms: Scavenge 1.9 (37.0) -> 0.9 (37.0) MB, 1.0 ms 54 ms: Scavenge 1.8 (36.0) -> 0.9 (36.0) MB, 2.9 ms 67 ms: Scavenge 1.8 (36.0) -> 0.9 (37.0) MB, 2.7 ms. 44
  45. 特征分析 45

  46. 任务分类 数组 [1, 2, 3] 对象 { a: 1, b:

    3 } 括号里的表达式 (x + y) 方法 function test() { } 标识 x 字面量 42 ? 46
  47. 原始分类 function parsePrimaryExpression() { if (match('[')) return parseArrayInitialiser(); if (match('{'))

    return parseObjectInitialiser(); if (match('(')) return parseBracketExpression(); if (matchKeyword('function')) return parseFunctionExpression() if (matchKeyword('this')) return createThisExpression(); if (match('/') || match('/=')) return createRegExpLiteral(); var token = lex(); if (token.type === Token.Identifier) return createIdentifier(token); if (token.type === Token.NullLiteral) return createNullLiteral(); if (token.type === Token.NumericLiteral) return createNumericLiteral(token); if (token.type === Token.StringLiteral) return createStringLiteral(token); if (token.type === Token.BooleanLiteral) return createBooleanLiteral(token); return throwUnexpected(token); } 47
  48. 输入分布统计 54362 Identifier 10419 Keyword 8170 String 5820 Punctuator 5213

    Numeric 1575 Boolean 909 Null 48
  49. 优化后的分类 function parsePrimaryExpression() { var token = lookahead(); if (token.type

    === Token.Identifier) return createIdentifier(token); if (token.type === Token.NumericLiteral) return createNumericLiteral(token); if (token.type === Token.StringLiteral) return createStringLiteral(token); if (matchKeyword('this')) return createThisExpression(); if (matchKeyword('function')) return parseFunctionExpression() if (token.type === Token.BooleanLiteral) return createBooleanLiteral(token); if (token.type === Token.NullLiteral) return createNullLiteral(); if (match('[')) return parseArrayInitialiser(); if (match('{')) return parseObjectInitialiser(); if (match('(')) return parseBracketExpression(); if (match('/') || match('/=')) return createRegExpLiteral(); return throwUnexpected(lex()); } 49
  50. 使用短路 http://ariya.ofilabs.com/2011/11/matching-a-decimal-digit.html function isDigit(ch) { return '0123456789'.indexOf(ch) >= 0; }

    function isDigit(ch) { return ch !==' ' && '0123456789'.indexOf(ch) >= 0; } 字符 数字 空格 50
  51. 对象集合 http://ariya.ofilabs.com/2012/08/determining-objects-in-a-set-examples-in-javascript.html var valid_words = { 'foobar': true, 'bar': true,

    'baz': true, 'quux': true }; function is_valid(word) { return valid_words.hasOwnProperty(word); } is_valid('fox'); // false 字典 拼写检查 可选替代方案: 数组查找, switch 语句, 前缀/后缀树, 完美散列 51
  52. 分支条件 function is_valid(word) { switch (word.length) { case 3: return

    word === 'bar' || word === 'baz'; case 4: return word === 'quux'; case 6: return word === 'foobar'; } return false; } 筛选 #1: 长度 http://ariya.ofilabs.com/2012/08/determining-objects-in-a-set-examples-in-javascript.html 52
  53. 可扩展性 53

  54. 快就够了? Alice Bob Chuck Dan ... Bob Alice Dan Chuck

    ... 通讯簿 排序 速度如何? 10 个联系人排序用时2 ms 54
  55. 实现代码 Array.prototype.swap = function (i, j) { var k =

    this[i]; this[i] = this[j]; this[j] = k; } function sort(list) { var items = list.slice(0), swapped = false, p, q; for (p = 1; p < items.length; ++p) { for (q = 0; q < items.length - p; ++q) { if (items[q + 1] < items[q]) { items.swap(q, q + 1); swapped =true; } } if (!swapped) break; } return items; } 冒泡排序 ??? 坑爹啊~~~ 55
  56. 用于调试的预留代码 Array.prototype.swap = function (i, j) { var k =

    this[i]; this[i] = this[j]; this[j] = k; } Array.prototype.swap = function (i, j) { Log({ name: 'Array.prototype.swap', lineNumber: 1}); var k = this[i]; this[i] = this[j]; this[j] = k; } http://ariya.ofilabs.com/2012/01/scalable-web-apps-the-complexity-issue.html 56
  57. 执行时间分析 0 250000 500000 0 1000 调用 swap() 500 参数规模

    http://ariya.ofilabs.com/2012/01/scalable-web-apps-the-complexity-issue.html 57
  58. 追踪程序启动时间 https://gist.github.com/1823129 jQuery Mobile 启动日志 4640 个方法调用 jquery.js 26 jQuery

    jquery.js 103 init undefined, undefined, [object Object] jquery.js 274 each (Function) jquery.js 631 each [object Object], (Function), undefined jquery.js 495 isFunction [object Object] jquery.js 512 type [object Object] jquery.mobile.js 1857 [Anonymous] jquery.mobile.js 642 [Anonymous] jquery.mobile.js 624 enableMouseBindings jquery.mobile.js 620 disableTouchBindings http://ariya.ofilabs.com/2012/02/tracking-javascript-execution-during-startup.html 58
  59. 结束语 59

  60. 多角度分析 应用升级 运行的时间消耗 基准线 60

  61. http://www.trollquotes.org/5-gandalf-troll-quote/ 61

  62. 三思而后行 高级技巧: 学习 & 探索 去运用 每一个 最佳实践 62

  63. 致谢 ariya.hidayat@gmail.com @AriyaHidayat ariya.ofilabs.com Many artworks are from OpenClipArt speakerdeck.com/ariya

    63
  64. 更多的资料 Vyacheslav Egorov http://mrale.ph/ Florian Loitsch http://floitsch.blogspot.com/ Andy Wingo http://wingolog.org/

    http://blip.tv/jsconf/jsconf2012-andy-wingo-6139109 Michael Starzinger: The Footprint of Performance http://www.youtube.com/watch?v=ZhshEZIV2F4 Google I/O 2012 - Breaking the JavaScript Speed Limit with V8 http://www.youtube.com/watch?v=UJPdhx5zTaw 64