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

Parcel2 v. Webpack - Build times are significantly slower #4566

Closed
astegmaier opened this issue May 4, 2020 · 6 comments
Closed

Parcel2 v. Webpack - Build times are significantly slower #4566

astegmaier opened this issue May 4, 2020 · 6 comments

Comments

@astegmaier
Copy link
Contributor

astegmaier commented May 4, 2020

🐛 bug report

I've started trying to use Parcel2 in a real-life situation, and I'm noticed that the parcel build times are significantly slower than webpack's, even with a warm cache, as long as there is at least one change since the last build. (It also makes significantly larger bundles - see #4565).

Like all real-life situations, it's complicated, so I tried to create a simplified repro environment in the fluentui-button-babel branch of this repo. It's a simple react app with a single Button component from the @fluentui/react-northstar library.

🎛 Configuration (.babelrc, package.json, cli command)

Webpack (full config here):

  • Using babel-loader with this config:
presets: [
   "@babel/preset-env",
   "@babel/preset-react",
   "@babel/preset-typescript",
]

Parcel configuration:

  • No .parcelrc file, which means that the default babel transformer is being used.
  • No custom babel configuration.
  • Ran parcel build src/index.html

src/index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Sample App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <script src="./index.tsx"></script>
  </body>
</html>

src/index.tsx:

import React from "react";
import ReactDOM from "react-dom";
import App from "./components/App";

ReactDOM.render(<App />, document.getElementById("root"));

src/components/App.ts:

import React from "react";
import { Provider, themes, Button } from "@fluentui/react-northstar";

const App = () => (
  <Provider theme={themes.teams}>
    <Button content="Hello from FluentUI" />
  </Provider>
);

export default App;

🤔 Expected Behavior

Parcel should be at least as fast as webpack, hopefully significantly faster if there is a warm cache.

😯 Current Behavior

Here's a comparison of Parcel v. Webpack build times in various scenarios.

Scenario Trial 1 Trial 2 Trial 3 Average
parcel build - no cache 44.8s 43.7s 46.7s 45.1s
parcel build - with cache, no changes 2.2s 2.2s 2.1s 2.2s
parcel build - with cache, one string change 24.1s 24.3s 23.2s 23.9s
webpack build - after clearing Terser cache 13.7s 13.2s 13.8s 13.6s
webpack build - with Terser cache, no changes 7.4s 7.6s 7.9s 7.6s
webpack build - with Terser cache, one string change 13.3s 13.2s 13.4s 13.3s

All scenarios (both parcel and webpack) are:

  • doing production builds (i.e. including tree-shaking, scope-hoisting, and minification)
  • using babel for transpilation (with default config in parcel and babel-loader for webpack).
  • generating sourcemaps.
  • not doing any typechecking.
  • On a MacBook Pro (13-inch, Early 2015) with 2.7 GHz Dual-Core Intel Core i5 and 16 GB RAM.

💁 Possible Solution

There are probably multiple ways to make things faster. One thing I noticed is that a lot of the time each build (even with a warm parcel cache) is spent during the "optimization" phase. It looks like TerserWebpackPlugin might be leveraging some caching that we are not. It might also be related to #4565 - if we could successfully tree-shake the stuff that webpack can, all the later steps would be faster.

🔦 Context

Trying to build a real-world app that uses @fluentui/react-northstar and parcel2.

💻 Code Sample

See the fluentui-button-babel branch of this repo.

🌍 Your Environment

Software Version(s)
Parcel 2.0.0-nightly.248
Node 12.16.3
Yarn 1.22.4
Operating System OSX 10.15.4
@mischnic
Copy link
Member

mischnic commented May 4, 2020

One thing I noticed is that a lot of the time each build (even with a warm parcel cache) is spent during the "optimization" phase

If you mean that you see "optimizing foo...." for a while, that could also mean (because optimizing and packaging happens in parallel):
packaging for bundle A started, packaging for bundle B started, optimizing for bundle A started, (now we're actually waiting for the packaging of bundle B).

@astegmaier
Copy link
Contributor Author

astegmaier commented May 4, 2020

If you mean that you see "optimizing foo...." for a while, that could also mean (because optimizing and packaging happens in parallel):
packaging for bundle A started, packaging for bundle B started, optimizing for bundle A started, (now we're actually waiting for the packaging of bundle B).

Thanks for the tip! In case it's helpful, I ran parcel with the --profile flag and uploaded the .trace files to dropbox:

  • Scenario 1: fresh parcel build with no cache - profile
  • Scenario 2: build parcel with warm cache and a single string change in the source files - profile.

@mischnic
Copy link
Member

mischnic commented May 4, 2020

Does "webpack build - with Terser cache" correspond to "no changes" or "one string change"?

BTW, it doesn't matter what you change (a string or something else), the JS AST handling is by far not intelligent enough to cache anything in that case, the packager has to run again.

Is it possible that the two trace files are swapped? The cold cache one should be larger than the warm cache run.

@astegmaier
Copy link
Contributor Author

astegmaier commented May 5, 2020

Does "webpack build - with Terser cache" correspond to "no changes" or "one string change"?

"no changes"

I clarified the table above and also tested the other scenario - "webpack build - with Terser cahce, one string change", and I got results similar to the "no terser cache" case (~13s on my machine).

Is it possible that the two trace files are swapped? The cold cache one should be larger than the warm cache run.

I'm pretty sure I didn't accidentally swap them, although I agree it is weird that something that should take more work would be smaller. Maybe something went wrong writing the profile to disk? (In an earlier attempt, I ran out of heap memory, and I noticed that it would still write something to disk, but it would be incomplete - maybe that happened, and I didn't notice?). To be extra sure, I ran it again, and here are links to the full set.

# Scenario First attempt (also linked above) Second attempt (new)
1 fresh parcel build with no cache profile1 (204.4MB) profile2 (544.2MB)
2 build parcel with warm cache and a change in the source files profile1 (492.7MB) profile2 (500.2MB)

Per @wbinnssmith's suggestion, I gziped all four traces for faster downloading. It's only 69MB combined (!).

@astegmaier
Copy link
Contributor Author

Based on what I learned investigating #4565 about how to bypass the tree-shaking issue, I re-ran the perf benchmarks on an app that uses the more-granular import style.

In these tests, I'm importing @fluentui/react-northstar components using the following syntax:

import Box from "@fluentui/react-northstar/dist/es/components/Box/Box";
import Provider from "@fluentui/react-northstar/dist/es/components/Provider/Provider";
import teamsTheme from "@fluentui/react-northstar/dist/es/themes/teams";

Parcel's bundle size is significantly smaller (631KiB) than in the original tests, and more comparable to webpack's (622Kib).

Scenario Trial 1 Trial 2 Trial 3 Average
parcel build - no cache 35.8s 33.5s 33.4s 34.2s
parcel build - with cache, no changes 2.1s 2.0s 2.1s 2.1s
parcel build - with cache, one string change 18.1s 19.3s 17.8s 18.4s
webpack build - after clearing Terser cache 12.1s 11.2s 11.3s 11.5s
webpack build - with Terser cache 6.2s 6.1s 6.2s 6.2s
webpack build - with Terser cache, one string change 11.5s 11.4s 11.2s 11.4s

So that sped things up significantly, but it's still about 50% slower than webpack in the "one-string change" case and 3x slower in the "no cache" case.

@devongovett
Copy link
Member

I think this should be mainly solved now after the latest rewrite

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

No branches or pull requests

3 participants