facebookarchive / prepack

A JavaScript bundle optimizer.
http://prepack.io
Other
14.22k stars 424 forks source link

factorifyObjects inefficiency #914

Open NTillmann opened 7 years ago

NTillmann commented 7 years ago

The factorifyObjects feature in Prepack reduces code size by creating factory functions for common object creation patterns. For example,

// Copies of x: 2
// ^ When present in a serializer test case, this comment will make the test case fail if the prepacked code only mentions x once, instead of twice
(function() {
    a = { x: 0, y: 1 };
    b = { x: 0, y: 1 };
    c = { x: 0, y: 1 };
    d = { x: 0, y: 1 };
    e = { x: 0, y: 1 };
    A = { x: 0, y: 2 };
    B = { x: 0, y: 2 };
    C = { x: 0, y: 2 };
    D = { x: 0, y: 2 };
    E = { x: 0, y: 2 };
})();

prepacks to

(function () {
  function $_2() {
    return $_0(0, 2);
  }
  function $_1() {
    return $_0(0, 1);
  }
  function $_0(__0, __1) {
    return {
      x: __0,
      y: __1
    };
  }
  var _0 = $_1();
  a = _0;
  var _1 = $_1();
  b = _1;
  var _2 = $_1();
  c = _2;
  var _3 = $_1();
  d = _3;
  var _4 = $_1();
  e = _4;
  var _5 = $_2();
  A = _5;
  var _6 = $_2();
  B = _6;
  var _7 = $_2();
  C = _7;
  var _8 = $_2();
  D = _8;
  var _9 = $_2();
  E = _9;
})();

While correct, the two levels of factory functions seems a bit excessive. We should consider simply inlining $_0. This would save a good thousand function calls in a medium FB internal JavaScript program.

jeffreytan81 commented 7 years ago

Doing this will increase the code size. Maybe we should leave the inline optimization to the JS compiler?

NTillmann commented 7 years ago

Indeed, we should measure first what relevant JS compilers actually inline.

NTillmann commented 6 years ago

I looked into it. For what matters most for Facebook, nothing gets inlined here. And calls to simple functions like $_0...$_2 look rather expensive. Prepack should inline $_0.

And when still parameters are involved, consider a scheme like the following best hidden class efficiency.

function $_0(__0, __1) {
    let __res = {
      x: undefined,
      y: undefined
    };
    __res.x = __0;
    __res.y = __1;
    return __res;
}