Skip to content
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

feat: several optimizations on minifier #3979

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

unbyte
Copy link

@unbyte unbyte commented Nov 16, 2024

Background

Our platform provides approximately hundreds of thousands of frontend builds each month for both internal and external developers. To balance machine resources and the size of the artifacts, we have adopted esbuild and optimized its minification strategy (details can be found in PRs from two years ago like #2614). These optimizations reduced the package size inflation rate compared to terser from 15% before optimization to around 4%.

Our use case primarily involved minifying ES5 code, so the previous implementation did indeed have some issues that were not exposed in our production environment. Earlier this year, I re-implemented the optimizations, which is why the old PR was closed and this PR comes.

Optimizations

please refer to the comments in the code for edge cases.

1. reuse names for non-overlapping symbols

// source
function test(impl) {
  if (!impl) {
    const helper = (fn, arg) => fn(arg);
    return (fn) => helper(fn, 1);
  }
  return () => impl();
}

// before
function test(e) {
  if (!e) {
    const n = (t, r) => t(r);
    return (t) => n(t, 1);
  }
  return () => e();
}

// after
function test(t) {
  if (!t) {
    const t = (t, e) => t(e);
    return (e) => t(e, 1);
  }
  return () => t();
}

2. allow inline vars

// source
function test() {
  var n = 1;
  return n;
}

// before
function test() {
  var n = 1;
  return n;
}

// after
function test() {
  return 1;
}

3. drop or rewrite unused variables

// source
function test() {
  var a = 1, b = someFn();
  const c = 2;
  let d;
  return 2;
}

// before
function test() {
  var a = 1, b = someFn();
  const c = 2;
  let d;
  return 2;
}

// after
function test() {
  return someFn(), 2;
}

Benchmarks

From https://github.com/privatenumber/minification-benchmarks, benchmark on MacBook Pro M1 Pro (10cores).

---
config:
    xyChart:
        width: 720
        height: 360
        xAxis:
            labelPadding: 20
        yAxis:
            labelPadding: 10
---
xychart-beta
	title "react v17.0.2"
	x-axis ["Original",1,2,3,4,5,6,7,8]
	y-axis "Gzip size" 0 --> 19385
	bar [19385,8173,8177,8265,8448,8506,8543,8668,8746]
Loading
Artifact Original size Gzip size
react v17.0.2 (Source) 72.13 kB 19.39 kB
Minifier Minified size Minzipped size Time
1. @swc/core -68% 22.87 kB 🏆-58% 8.17 kB 🏆 16 ms
2. uglify-js 🏆-69% 22.64 kB -58% 8.18 kB 14x 223 ms
3. terser -68% 23.07 kB -57% 8.27 kB 8x 130 ms
4. babel-minify -67% 23.60 kB -56% 8.45 kB 21x 344 ms
5. esbuild-pr -67% 23.55 kB -56% 8.51 kB 4x 75 ms
6. esbuild -67% 23.70 kB -56% 8.54 kB 4x 70 ms
7. uglify-js (no compress) -65% 25.03 kB -55% 8.67 kB 2x 44 ms
8. terser (no compress) -65% 25.08 kB -55% 8.75 kB 3x 55 ms

---
config:
    xyChart:
        width: 720
        height: 360
        xAxis:
            labelPadding: 20
        yAxis:
            labelPadding: 10
---
xychart-beta
	title "moment v2.29.1"
	x-axis ["Original",1,2,3,4,5,6,7,8]
	y-axis "Gzip size" 0 --> 36231
	bar [36231,18568,18687,18690,19119,19308,19334,19569,19683]
Loading
Artifact Original size Gzip size
moment v2.29.1 (Source) 173.90 kB 36.23 kB
Minifier Minified size Minzipped size Time
1. uglify-js 🏆-67% 57.73 kB 🏆-49% 18.57 kB 18x 505 ms
2. @swc/core -67% 58.21 kB -48% 18.69 kB 🏆 27 ms
3. terser -66% 59.14 kB -48% 18.69 kB 10x 274 ms
4. babel-minify -66% 59.70 kB -47% 19.12 kB 27x 737 ms
5. esbuild-pr -66% 59.73 kB -47% 19.31 kB 2x 70 ms
6. esbuild -66% 59.82 kB -47% 19.33 kB 2x 75 ms
7. uglify-js (no compress) -64% 62.50 kB -46% 19.57 kB 3x 92 ms
8. terser (no compress) -64% 63.15 kB -46% 19.68 kB 4x 121 ms

---
config:
    xyChart:
        width: 720
        height: 360
        xAxis:
            labelPadding: 20
        yAxis:
            labelPadding: 10
---
xychart-beta
	title "jquery v3.5.1"
	x-axis ["Original",1,2,3,4,5,6,7,8]
	y-axis "Gzip size" 0 --> 84498
	bar [84498,30867,30903,30912,31058,31470,31621,31799,31954]
Loading
Artifact Original size Gzip size
jquery v3.5.1 (Source) 287.63 kB 84.50 kB
Minifier Minified size Minzipped size Time
1. @swc/core -69% 89.15 kB 🏆-63% 30.87 kB 🏆 46 ms
2. uglify-js 🏆-69% 88.45 kB -63% 30.90 kB 15x 716 ms
3. terser -69% 89.54 kB -63% 30.91 kB 7x 366 ms
4. esbuild-pr -69% 89.55 kB -63% 31.06 kB 1x 74 ms
5. uglify-js (no compress) -67% 94.08 kB -63% 31.47 kB 2x 127 ms
6. terser (no compress) -67% 94.55 kB -63% 31.62 kB 3x 153 ms
7. babel-minify -68% 92.10 kB -62% 31.80 kB 27x 1,254 ms
8. esbuild -69% 90.07 kB -62% 31.95 kB 1x 65 ms

---
config:
    xyChart:
        width: 720
        height: 360
        xAxis:
            labelPadding: 20
        yAxis:
            labelPadding: 10
---
xychart-beta
	title "vue v2.6.12"
	x-axis ["Original",1,2,3,4,5,6,7,8]
	y-axis "Gzip size" 0 --> 89668
	bar [89668,42522,42919,43036,43659,43925,44368,44450,44679]
Loading
Artifact Original size Gzip size
vue v2.6.12 (Source) 342.15 kB 89.67 kB
Minifier Minified size Minzipped size Time
1. @swc/core -66% 115.50 kB 🏆-53% 42.52 kB 🏆 66 ms
2. terser -66% 116.80 kB -52% 42.92 kB 7x 470 ms
3. uglify-js 🏆-67% 113.80 kB -52% 43.04 kB 14x 970 ms
4. esbuild-pr -66% 117.61 kB -51% 43.66 kB 1x 73 ms
5. babel-minify -66% 117.90 kB -51% 43.93 kB 21x 1,380 ms
6. esbuild -65% 118.14 kB -51% 44.37 kB 1x 67 ms
7. uglify-js (no compress) -63% 126.14 kB -50% 44.45 kB 2x 157 ms
8. terser (no compress) -63% 126.58 kB -50% 44.68 kB 3x 202 ms

---
config:
    xyChart:
        width: 720
        height: 360
        xAxis:
            labelPadding: 20
        yAxis:
            labelPadding: 10
---
xychart-beta
	title "lodash v4.17.21"
	x-axis ["Original",1,2,3,4,5,6,7,8]
	y-axis "Gzip size" 0 --> 96690
	bar [96690,24686,25156,25186,25503,25862,26062,26200,26221]
Loading
Artifact Original size Gzip size
lodash v4.17.21 (Source) 544.09 kB 96.69 kB
Minifier Minified size Minzipped size Time
1. uglify-js 🏆-87% 68.17 kB 🏆-74% 24.69 kB 12x 773 ms
2. @swc/core -87% 69.84 kB -74% 25.16 kB 🏆 62 ms
3. terser -87% 70.67 kB -74% 25.19 kB 6x 409 ms
4. babel-minify -87% 72.37 kB -74% 25.50 kB 17x 1,096 ms
5. uglify-js (no compress) -86% 74.61 kB -73% 25.86 kB 2x 144 ms
6. esbuild-pr -87% 72.09 kB -73% 26.06 kB 1x 75 ms
7. esbuild -87% 72.48 kB -73% 26.20 kB 1x 71 ms
8. terser (no compress) -86% 75.29 kB -73% 26.22 kB 2x 170 ms

---
config:
    xyChart:
        width: 720
        height: 360
        xAxis:
            labelPadding: 20
        yAxis:
            labelPadding: 10
---
xychart-beta
	title "d3 v6.3.1"
	x-axis ["Original",1,2,3,4,5,6,7]
	y-axis "Gzip size" 0 --> 130686
	bar [130686,87016,87207,88087,88319,88669,89156,90800]
Loading
Artifact Original size Gzip size
d3 v6.3.1 (Source) 555.77 kB 130.69 kB
Minifier Minified size Minzipped size Time
1. uglify-js 🏆-53% 263.56 kB 🏆-33% 87.02 kB 18x 1,808 ms
2. @swc/core -52% 265.26 kB -33% 87.21 kB 1x 142 ms
3. terser -52% 267.77 kB -33% 88.09 kB 10x 1,009 ms
4. uglify-js (no compress) -50% 275.35 kB -32% 88.32 kB 3x 314 ms
5. esbuild-pr -52% 269.54 kB -32% 88.67 kB 1x 122 ms
6. terser (no compress) -50% 276.47 kB -32% 89.16 kB 4x 462 ms
7. esbuild -51% 270.13 kB -31% 90.80 kB 🏆 96 ms
8. babel-minify ❌ Minification -

---
config:
    xyChart:
        width: 720
        height: 360
        xAxis:
            labelPadding: 20
        yAxis:
            labelPadding: 10
---
xychart-beta
	title "terser v5.30.3"
	x-axis ["Original",1,2,3,4,5,6,7]
	y-axis "Gzip size" 0 --> 193763
	bar [193763,123095,123334,123482,123612,124428,124609,126706]
Loading
Artifact Original size Gzip size
terser v5.30.3 (Source) 1.01 MB 193.76 kB
Minifier Minified size Minzipped size Time
1. @swc/core -55% 455.60 kB 🏆-36% 123.10 kB 1x 126 ms
2. uglify-js 🏆-55% 451.19 kB -36% 123.33 kB 17x 1,715 ms
3. terser -55% 458.29 kB -36% 123.48 kB 9x 932 ms
4. esbuild-pr -55% 456.94 kB -36% 123.61 kB 1x 106 ms
5. terser (no compress) -53% 474.40 kB -36% 124.43 kB 4x 432 ms
6. uglify-js (no compress) -53% 472.16 kB -36% 124.61 kB 3x 361 ms
7. esbuild -55% 458.89 kB -35% 126.71 kB 🏆 97 ms
8. babel-minify ❌ Minification -

---
config:
    xyChart:
        width: 720
        height: 360
        xAxis:
            labelPadding: 20
        yAxis:
            labelPadding: 10
---
xychart-beta
	title "three v0.124.0"
	x-axis ["Original",1,2,3,4,5,6,7,8]
	y-axis "Gzip size" 0 --> 248267
	bar [248267,158497,159071,159198,160696,162498,163036,163198,163725]
Loading
Artifact Original size Gzip size
three v0.124.0 (Source) 1.25 MB 248.27 kB
Minifier Minified size Minzipped size Time
1. @swc/core -48% 645.27 kB 🏆-36% 158.50 kB 1x 194 ms
2. uglify-js 🏆-49% 641.59 kB -36% 159.07 kB 19x 2,334 ms
3. terser -48% 653.25 kB -36% 159.20 kB 10x 1,278 ms
4. esbuild-pr -48% 643.82 kB -35% 160.70 kB 1x 125 ms
5. babel-minify -48% 648.83 kB -35% 162.50 kB 72x 8,440 ms
6. uglify-js (no compress) -46% 674.49 kB -34% 163.04 kB 3x 467 ms
7. terser (no compress) -46% 675.50 kB -34% 163.20 kB 5x 600 ms
8. esbuild -48% 646.76 kB -34% 163.73 kB 🏆 117 ms

---
config:
    xyChart:
        width: 720
        height: 360
        xAxis:
            labelPadding: 20
        yAxis:
            labelPadding: 10
---
xychart-beta
	title "victory v35.8.4"
	x-axis ["Original",1,2,3,4,5,6,7]
	y-axis "Gzip size" 0 --> 309942
	bar [309942,157435,158055,158706,162471,166386,167579,181071]
Loading
Artifact Original size Gzip size
victory v35.8.4 (Source) 2.13 MB 309.94 kB
Minifier Minified size Minzipped size Time
1. uglify-js 🏆-67% 694.78 kB 🏆-49% 157.44 kB 23x 3,094 ms
2. @swc/core -67% 712.61 kB -49% 158.06 kB 2x 280 ms
3. terser -66% 715.58 kB -49% 158.71 kB 13x 1,825 ms
4. esbuild-pr -66% 719.43 kB -48% 162.47 kB 1x 148 ms
5. terser (no compress) -64% 759.34 kB -46% 166.39 kB 5x 756 ms
6. uglify-js (no compress) -65% 756.53 kB -46% 167.58 kB 4x 625 ms
7. esbuild -66% 724.14 kB -42% 181.07 kB 🏆 133 ms
8. babel-minify ❌ Minification -

---
config:
    xyChart:
        width: 720
        height: 360
        xAxis:
            labelPadding: 20
        yAxis:
            labelPadding: 10
---
xychart-beta
	title "echarts v5.1.1"
	x-axis ["Original",1,2,3,4,5,6,7]
	y-axis "Gzip size" 0 --> 684611
	bar [684611,320267,321987,325222,326944,330736,331412,331563]
Loading
Artifact Original size Gzip size
echarts v5.1.1 (Source) 3.20 MB 684.61 kB
Minifier Minified size Minzipped size Time
1. @swc/core -69% 993.25 kB 🏆-53% 320.27 kB 2x 523 ms
2. terser -69% 1.00 MB -53% 321.99 kB 16x 2,953 ms
3. esbuild-pr -68% 1.01 MB -52% 325.22 kB 1x 198 ms
4. uglify-js 🏆-69% 979.10 kB -52% 326.94 kB 32x 5,788 ms
5. terser (no compress) -66% 1.07 MB -52% 330.74 kB 7x 1,340 ms
6. uglify-js (no compress) -67% 1.07 MB -52% 331.41 kB 5x 890 ms
7. esbuild -68% 1.01 MB -52% 331.56 kB 🏆 175 ms
8. babel-minify ❌ Timed out - - ⚠️ +10,000 ms

---
config:
    xyChart:
        width: 720
        height: 360
        xAxis:
            labelPadding: 20
        yAxis:
            labelPadding: 10
---
xychart-beta
	title "antd v4.16.1"
	x-axis ["Original",1,2,3,4,5,6,7]
	y-axis "Gzip size" 0 --> 825175
	bar [825175,452625,454517,457786,463719,475480,478572,488279]
Loading
Artifact Original size Gzip size
antd v4.16.1 (Source) 6.67 MB 825.18 kB
Minifier Minified size Minzipped size Time
1. uglify-js -67% 2.20 MB 🏆-45% 452.63 kB 24x 5,720 ms
2. @swc/core 🏆-67% 2.18 MB -45% 454.52 kB 2x 676 ms
3. terser -66% 2.25 MB -45% 457.79 kB 14x 3,332 ms
4. esbuild-pr -66% 2.27 MB -44% 463.72 kB 1x 256 ms
5. terser (no compress) -64% 2.43 MB -42% 475.48 kB 6x 1,591 ms
6. uglify-js (no compress) -64% 2.42 MB -42% 478.57 kB 5x 1,266 ms
7. esbuild -65% 2.31 MB -41% 488.28 kB 🏆 232 ms
8. babel-minify ❌ Timed out - - ⚠️ +10,000 ms

---
config:
    xyChart:
        width: 720
        height: 360
        xAxis:
            labelPadding: 20
        yAxis:
            labelPadding: 10
---
xychart-beta
	title "typescript v4.9.5"
	x-axis ["Original",1,2,3,4,5,6]
	y-axis "Gzip size" 0 --> 1884998
	bar [1884998,851747,854211,868230,876535,879301,915495]
Loading
Artifact Original size Gzip size
typescript v4.9.5 (Source) 10.95 MB 1.88 MB
Minifier Minified size Minzipped size Time
1. @swc/core 🏆-70% 3.31 MB 🏆-55% 851.75 kB 3x 1,316 ms
2. terser -69% 3.35 MB -55% 854.21 kB 20x 7,200 ms
3. esbuild-pr -69% 3.38 MB -54% 868.23 kB 1x 399 ms
4. uglify-js (no compress) -68% 3.54 MB -53% 876.54 kB 6x 2,107 ms
5. terser (no compress) -68% 3.53 MB -53% 879.30 kB 7x 2,734 ms
6. esbuild -68% 3.49 MB -51% 915.50 kB 🏆 351 ms
7. babel-minify ❌ Timed out - - ⚠️ +10,000 ms
8. uglify-js ❌ Timed out - - ⚠️ +10,000 ms

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.

1 participant