zoukankan      html  css  js  c++  java
  • JavaScript 性能优化 --By Google V8 Team Manager

    原文:https://developers.google.com/v8/?hl=zh-CN

    Be Prepared before writing code[9:35]

    Understand how V8 optimzes Javascript;

    Write code mindfully;

    Learn you tools and how they can help you;

    Be Prepared - Hidden Classes[10:30]

    Hidden Classes Make JavaScript Faster.

    (注:Hidden Class 可以理解为VM内部实现对描述抽象类的描述,共享隐藏类才能让VM更高效)

    Limit Compile-time type information

    It's expensive to reason about JavaScript types at compile time...

    • V8 internally creates hidden classes for objects at runtime.
    • Objects with the same hidden class can use the same optimzed generated code.

    Initialize all object members in constructor functions;

    Always Initialize members in the same order; 

    (If you add members in different orders, you create a different tree of hidden classes.

    And at the end, you'll have objects with two different hidden classes that can't use the same optimized code)

    Be Prepared - Numbers[15:30]

    We use a technique called tagging. So inside of V8 we pass around values of 32-bit numbers and objects.

    But we want to be able to use the same 32 bits to represent both. And that way we can have one code path that can handle, in many cases, the objects and integers. So what we do is we use the bottom bit.

    And each of the values have s special meaning. If the bit is se, it's an object pointer. If it's clear, it's what we call small integer or smi. And that's a 31-bit signed integer. Now if you have a numeric value that you're passing around, assigning to a member that is bigger -it's a numeric value that's bigger than 31 signed bits - then it doesn't fit in one of these smis. And we have to create what's called a box for it. We box the number. We turn it into a double and we create a new object to put that number inside of it. And what follows from that is the next speed trap to avoid, which is make sure, whenever possible, you use 31-bit signed numbers for performance critical calculations.

    Prefer numberic values that can be represented as 31-bit signed integers.

    Be prepared - Arrays[17:25]

    • Fast Elements: linear storage for compact key sets.
    • Dictionary Elements: hash table storage otherwise.

    Use contiguous keys starting at 0 for Arrays. (

    Don't pre-allocate large Arrays(e.g. > 64K elements) to their maxium size, instead grow as you go.

    Don't delete elements in arrays, especially numberic arrays.

    Double Array Unboxing

    • Array's hidden class tracks elements types
    • Arrays contraining only doubles are unboxed
    • Unboxing causes hidden class change

    Initialize using array literals for small fixed-sized arrays

    Preallocate small arrays to correct size before using them

    Don't store non-numeric values(objects) in numeric arrays

    Be prepared - Full Compiler[26:36]

    V8 has tow compilers

    • "Full" compiler can generate good code for any Javascript
    • Optimizing compiler produces great code for most JavaScript

    "Full" compiler Starts Executing Code ASAP

    • Quickly generates good but not great JIT code
    • Assumes(almost) nothing about types at compilation time
    • Uses Inline Caches(or ICs) to refine knowledge about types while program runs

    Inline Caches(ICs) handle Types Efficiently

    • Type-dependent code for operations
    • Validate type assumptions first, then do work
    • Change at runtime via backpathcing as more types are discovered

    Monomorphic Better Than Polymophic 

    • Operations are monomophic if the hidden class is always the same
    • Otherwise they are polymorphic
    function add(x,y) {
     return x + y;
    }
    
    add(1,2);         //+ in add is monomorphic
    add("a", "b")    //+ in add becomes polymorphic

    Prefer monomorphic over polymorphic whenever is possible.

    Type Feedback Makes Code Faster

    • Types taken from ICs
    • Operations speculatively get inlined
    • Monomophic functions and constructors can be inlined entirely
    • Inlininig enables other optimizations

    Logging What Gets Optimized

    d8 --trace-opt prime.js

    logs names of optimized functions to stdout

    Not Everything Can Be Optimized

    Some features prevent the optimizing compiler from running(a "bail-out")

    Avoid the speed trap

    Optimizing compiler "bail-out" on functions with try{} catch{} blocks.

    Maximizing Performance With Exceptions

    function perf_sensitive() {
      //Do performance-sensitive work here
    }
    try{
        perf_sensitive()
    } catch(e) {
     //handle exceptions here
    }

    How to Find Bailouts

    d8 --trace-bailout prime.js

    logs optimizing compiler bailouts

    Invalid assumptions lead to deoptimization[37:55]

    Deoptimization...

    • ...throws away optimized code
    • ...resumes execution at the right place in "full" compiler code
    • Reoptimization might be triggered again later, but for the short term, execution slows down.

    Passing V8 Options to Chrome

    "/Applicaitons/Google Chrome.app/Contents/MacOS/Google Chrome" --js-flags="--trace-opt --trace-deopt --trace-bailout"

    Avoid the speed trap

    Avoid hidden class changes in functions after they are optimized

    Identify and Understand[39:50]

    "Identify and Understand" for V8

    • Ensure problem is JavaScript
    • Reduce to pure JavaScript(no DOM!)
    • Collect metrics
    • Locate bottleneck(s)

    Prime Generator -- Profile It

    %out/ia32.release/d8 prime.js --prof
    
    287107

    using teh built-in sampling profiler

    • Takes sampe every millisecond
    • Writes v8.log

    What to expect from the primes Code

    function Primes() {
     ...
    this.addPrime = function(i) {
      this.primes[this.prime_count++] = i;
    }
    
    this.isPrimeDivisible = function(candidate) {
      for(var i = 1; i <= this.prime_count; ++i) {
        if(candidate % this.primes[i]) == 0) {
          return true;
        }
      }
      return false;
    }
    };
    
    function main() {
      p = new Primes();
      var c = 1;
      while (p.getPrimeCount() < 25000) {
        if(!p.isPrimeDivisible(c)) {
          p.addPrime(c);
        }
        c++;
      }
      print(p.getPrime(p.getPrimeCount()-1));
    }

    Prediction: Most Time Spent in main

    • All properties and functions monomorphic
    • All numeric operations are SMIs
    • All functions can be inlined
    • No deoptimizations or bailouts

    (输出省略 @42:50)

    Can you spot the bug?

    this.isPrimeDivisible = function(candidate) {
      for(var i = 1 ; i <= this.prime_count; ++i) {
        if (candidate % this.primes[i] == 0) return true;  
      }
      return false;
    }

    (Hint: primes is an array of length prime_count)

    % out/ia32.release/d8 primes-2.js --prof
    287107
    
    (省略)

    JavaScript is 60% faster than C++

    C++ 

    % g++ primes.cc -o primes
    % time ./primes
    287107
    real  0m2.955s
    user  0m2.952s
    sys   0m.001s

    JavaScript

    % time d8 primes-2.js
    287107
    real 0m1.829s
    user 0m1.827s
    sys  0m0.010s

    JavaScript is 17% slower than optimized C++

    Fix What Matters[49:59]

    Optimize Your Algorithm

    this.isPrimeDivisible = function(candidate) {
      for(var i = 1 ; i < this.prime_count; ++i) {
        var current_prime = this.primes[i];
        if(current_prime*current_prime > candidate){
          return false;
        }
        if (candidate % this.primes[i] == 0) return true;  
      }
      return false;
    }

    Final Results

    (输出省略)

    That's more than a 350x Speed-up!

    Keep Your Eyes on the Road

    • Be prepared
    • Identify and Understand the Crux
    • Fix What matters
  • 相关阅读:
    Final发布
    Final发布 视频展示
    Final发布 文案+美工展示
    PSP总结报告
    作业 20181204-1 每周例行报告
    对团队成员公开感谢
    作业 20181127-1 附加作业 软件工程原则的应用实例分析
    作业 20181127-2 每周例行报告
    作业 20181120-1 每周例行报告
    作业 20181113-2 每周例行报告
  • 原文地址:https://www.cnblogs.com/Tue/p/5505618.html
Copyright © 2011-2022 走看看