Skip to content

Minification: Create local variable for prototype during class declaration #16469

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from

Conversation

dmichon-msft
Copy link
Contributor

Creates a local variable storing a class's prototype to support better minification of class declarations when emitting for ES5 and below.

Example

Source

class A {
    a(): void { }
    b(): void { }
    get c(): { return 1; }
}

Old ES5 emit

var A = (function () {
    function A() {
    }
    A.prototype.a = function () { };
    A.prototype.b = function () { };
    Object.defineProperty(A.prototype, "c", {
        get: function () { return 1; },
        enumerable: true,
        configurable: true
   });
}());

New ES5 Emit

var A = (function () {
    function A() {
    }
    var proto_1 = A.prototype;
    proto_1.a = function () { };
    proto_1.b = function () { };
    Object.defineProperty(proto_1, "c", {
        get: function () { return 1; },
        enumerable: true,
        configurable: true
   });
}());

Fixes #9638

@alfaproject
Copy link

Isn't this worse after compression? Do you have some numbers?

@DanielRosenwasser
Copy link
Member

Hey @dmichon-msft, I think @alfaproject is correct. When using gzip compression, my understanding is that you end up with a table referencing these repeated segments anyway. Now, you have the same table lookup with additional code for the prototype alias that's inserted.

aving tried the given samples out on this page it looks like this change is less efficient. Do you have concrete numbers indicating otherwise?

@dmichon-msft
Copy link
Contributor Author

dmichon-msft commented Jun 14, 2017

@alfaproject @DanielRosenwasser

So, for a single, small class, this change results in slightly larger files after gzip. All following analytics are conducted by assuming that the minifier will perform optimum renaming of variables for maximum compressibility, then passing them through this page that Daniel linked.

Single, small class:

var A = (function () {
    function A() {
    }
    A.prototype.a = function () { };
    A.prototype.b = function () { };
    Object.defineProperty(A.prototype, "c", {
        get: function () { return 1; },
        enumerable: true,
        configurable: true
   });
}());

Result: 273 bytes before, 163 bytes after gzip
vs.

var A = (function () {
    function A() {
    }
    var a = A.prototype;
    a.a = function () { };
    a.b = function () { };
    Object.defineProperty(a, "c", {
        get: function () { return 1; },
        enumerable: true,
        configurable: true
   });
}());

Result: 268 bytes before, 168 bytes after gzip
Winner: before change, by 5 bytes/168 ~ 3%

If we add a couple more classes:

var A = (function () {
    function A() {
    }
    A.prototype.a = function () { };
    A.prototype.b = function () { };
    Object.defineProperty(A.prototype, "c", {
        get: function () { return 1; },
        enumerable: true,
        configurable: true
   });
}());

var B = (function () {
    function A() {
    }
    A.prototype.c = function () { };
    A.prototype.d = function () { };
    A.prototype.e = function () { };
    A.prototype.f = function () { };
}());

var C = (function () {
    function A() {
    }
    A.prototype.g = function () { };
    A.prototype.h = function () { };
    A.prototype.i = function () { };
    A.prototype.j = function () { };
    A.prototype.k = function () { };
}());

Result: 716 bytes before, 206 bytes after gzip
vs

var A = (function () {
    function A() {
    }
    var a = A.prototype;
    a.a = function () { };
    a.b = function () { };
    Object.defineProperty(a, "c", {
        get: function () { return 1; },
        enumerable: true,
        configurable: true
   });
}());

var B = (function () {
    function A() {
    }
    var a = A.prototype;
    a.c = function () { };
    a.d = function () { };
    a.e = function () { };
    a.f = function () { };
}());

var C = (function () {
    function A() {
    }
    var a = A.prototype;
    a.g = function () { };
    a.h = function () { };
    a.i = function () { };
    a.j = function () { };
    a.k = function () { };
}());

Results: 671 bytes before, 206 bytes after gzip
Winner: neither.

Adding more classes or members from this point gradually leans in favor of the change, though the delta is relatively small.

If I change the starting assumption about the minifier performing optimal variable renaming, then things get a bit stranger. I attempted to invoke the modified compiler against a much larger internal project, but due to some difficulties my numbers for the modified compiler include upstream files still built using the old compiler, and more local files build with the modified version.

Nonetheless, some partial numbers for some real world projects (all post gzip):

  • 479.26 KB -> 479.08 KB
  • 554.55 KB -> 554.41 KB
  • 496.75 KB -> 496.63 KB
  • 556.62 KB -> 556.44 KB
  • 458.36 KB -> 458.16 KB (largest improvement, at 0.20 KB)
  • 289.69 KB -> 289.74 KB (largest regression, at 0.05 KB)

Edit: it should be noted that in all tested scenarios the pre-gzip size is smaller with the modification.

@dmichon-msft
Copy link
Contributor Author

So, to sum up, gzip is pretty fantastic at compressing away .prototype., but there is still some room for improvement, and this is a pretty minor change. It also results in a reduction in uncompressed code footprint, which is unfortunately rather difficult to usefully quantify the effects of.

@esprehn
Copy link

esprehn commented Jul 13, 2017

This should also result in a small runtime performance improvement by avoiding the need to lookup the prototype property repeatedly. The engine's optimizer might be able to help once it warms up, but this code likely all runs during application startup while you're inside the interpreter.

@microsoft microsoft deleted a comment from msftclas Sep 27, 2017
@typescript-bot
Copy link
Collaborator

Thanks for your contribution. This PR has not been updated in a while and cannot be automatically merged at the time being. For housekeeping purposes we are closing stale PRs. If you'd still like to continue working on this PR, please leave a message and one of the maintainers can reopen it.

@bigopon
Copy link

bigopon commented Jun 1, 2019

Can we revive this PR anyone?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Suggestion: put prototype in var for better minification
8 participants