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

No performance improvement on rebuild with new cache #387

Open
spalger opened this issue Jun 23, 2018 · 5 comments
Open

No performance improvement on rebuild with new cache #387

spalger opened this issue Jun 23, 2018 · 5 comments
Milestone

Comments

@spalger
Copy link

spalger commented Jun 23, 2018

Expected Behavior

I expected that after the initial build populated a brand new cache that rebuilds (using the watch compiler) would see a performance improvement.

Actual Behavior

It seems that the initial build must be from cache in order to see a performance benefit, so when webpack starts without a usable cache for HardSource to use I must restart the webpack compiler process before I start to see build improvements.

without initial cache with initial cache
initial build ~72sec ~13sec
rebuilds ~20sec ~ 4sec

Is an error being thrown?

No

Steps to Reproduce

I'm not sure how to setup a reproduction large enough to demonstrate this behavior, but if you'd like to try running the PR I'm working on to integrate HardSource into our project I can share steps for that: elastic/kibana#20105

Operating System, Node, and NPM dependency versions

Mac OS 10.13.5
Node v8.11.3

See dependency versions here: https://github.com/spalger/kibana/blob/ab55f8ae15278bee1d700b901a4f38bf06cd14b2/package.json

@spalger
Copy link
Author

spalger commented Jun 23, 2018

Since it's entirely possible this is intended behavior, is there a way to know when HardSource is done writing it's initial cache so we can automatically restart our webpack process and not require user intervention to enable the beautiful rebuild speeds we could be seeing with HardSource?

@mzgoddard
Copy link
Owner

@spalger Thank you for this info. I haven't noticed this difference myself. Or at least not such a large difference.

My first guess I would try is dumping the memory cache webpack builds during the first run. During rebuilds webpack is using the modules it stores in there instead of the modules being returned by the module factory. In the rebuild those returned modules are from hard-source. Dumping the cache should make rebuilds similar to a first build with a hard-source cache.

@mzgoddard mzgoddard added this to the 0.8 milestone Jun 23, 2018
@spalger
Copy link
Author

spalger commented Jun 25, 2018

Alright, I took a shot at implementing your suggestion and you're right, after the second compilation rebuilds are as fast as they are when starting with a populated hard-source cache.

To implement this I used the following, which I arrived to by inspecting the hard-source code and logging the modules that were being removed from the cache repetitively (meaning they aren't being restored by hard-source and getting module.cacheItem defined).

this.compiler.hooks.done.tap({
  name: 'kibana-flushNonHardSourceCache',
  fn: (stats) => {
    // after a compilation runs for the first time we look through the
    // compilation cache and remove items that are cached by the
    // hard-source-webpack-plugin, leading them to be recreated on the
    // next compilation and behave much faster on the third compilation
    const { compilation } = stats;
    for (const [key, module] of Object.entries(compilation.cache)) {
      if (!module) {
        continue;
      }

      if (module.cacheItem) {
        // item was restored by hard-source-weback-plugin
        // so don't flush it from the cache
        continue;
      }

      // try to identify if this module is eligible for hard-source caching
      const probablyCachedByHardSource = (
        // only things webpack considers cacheable
        (module.buildInfo ? module.buildInfo.cacheable : module.cacheable)

        // ignored modules are cachable, but hard-source ignores them too
        && !key.startsWith('mignored ')

        // modules from mini-css-extract are ignored by hard-source because
        // of how they are integrated with webpack
        && !(
          key.startsWith('mcss ')
          || key.startsWith('mini-css-extract-plugin.')
          || key.match(/mini-css-extract-plugin[\\/]dist[\\/]loader.js/)
        )
      );

      if (probablyCachedByHardSource) {
        compilation.cache[key] = null;
      }
    }
  }
});

I wasn't able to easily identify if a compilation was as rebuild, or if there was a hard-source cache to rely on, so instead I opted for a general cache filter that looks for modules which will probably be replaced with hard-source-backed versions if they are removed from the cache.

I'm concerned about my check to define probablyCachedByHardSource. While it works today, for my specific configuration, I think hard-source could provide this information by flagging modules that are frozen and written to the cache. It could even do something like what I'm doing by default, ejecting modules that were freshly frozen from the compilation cache at the end of a compilation.

Would you be interested in such a feature?

@mzgoddard
Copy link
Owner

Yeah I'd like to add this as SystemForceHardSource.js.

A few things to change:

  1. Need to do this on child compilations too. That'll help compilations like html-webpack-plugin and mini-css-extract-plugin use. You could use the hook used to write out the cache to also operate on all compilations. Otherwise you could walk the compilation.children tree.
  2. Test that the module is a NormalModule. hard-source only caches NormalModules that are cacheable. I wouldn't worry too much about throwing away cached modules that hard-source doesn't cache. Such modules build very quickly. Testing that it is a module will also help to make sure you don't throw away child compilations which are on compilation.cache too.

@spalger
Copy link
Author

spalger commented Jun 27, 2018

Okay, great, I was filtering by NormalModules at some point but found things like the ignored modules were being recreated every time. You're right though, it's not worth worrying about webpack recreating them. Mind pointing me to "the hook used to write out the cache"?

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

No branches or pull requests

2 participants