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

Transpile in main thread #390

Merged
merged 1 commit into from
Jan 2, 2016

Conversation

jamestalmage
Copy link
Contributor

Manual reimplementation of #349.

This moves Babel transforms to the main process, and caches the transpiled results.

It uses caching-transform to provide caching. Expensive requires (like babel) are placed inside the factory function, which caching-transform only calls when it determines it needs to transform a file.

The path to each precompiled test is passed in the options object to fork:

fork('/path/to/file.js', {
  precompiled: {
    '/path/to/file.js': '/path/to/cached/file.js'
  }
});

It uses require-precompiled to install a require hook that pulls the transformed file from the cache.

The caching directory is used by the users package.json, and installing in node_modules/.cache/ava.
If the cache directory cannot be found (i.e. if they have no package.json), or the user provides a --no-cache flag, pre-compilation still happens on the main thread - but results are published to a temp directory.

@jamestalmage
Copy link
Contributor Author

Using got 6.0.0 branch:

master - first run (no cache)

./node_modules/.bin/ava  22.74s user 1.81s system 372% cpu 6.587 total

master - second run (with cache)

./node_modules/.bin/ava  6.65s user 0.77s system 175% cpu 4.240 total

this branch - first run (no cache)

./node_modules/.bin/ava  7.78s user 0.79s system 132% cpu 6.474 total

this branch - second run (w /cache)

./node_modules/.bin/ava  6.76s user 0.77s system 178% cpu 4.226 total

Huge advantage when the cache is empty.

@jamestalmage jamestalmage changed the title [WIP] transpile in main thread Transpile in main thread Dec 30, 2015
@jamestalmage
Copy link
Contributor Author

@sindresorhus, @vdemedes, et al. - ready for review.


function CachingPrecompiler(cacheDir) {
if (!(this instanceof CachingPrecompiler)) {
throw new Error('CachingPrecompiler must be called with new');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jamestalmage: What we do everywhere else is just invoke ourself with new and return it. Maybe switch to that?:

if (!(this instanceof CachingPrecompiler)) {
    return new CachingPrecompiler(cacheDir);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah sure. Probably worth matching style.

That said, after doing it the way you suggest for a long time, I am starting to think it's a mistake. ES2015 classes throw when function called, so magic auto-newing is not future proof. Also, it introduces an easy to overlook failure point when refactoring the function signature. I know I have forgotten to update that statement inside the if condition before.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer being explicit and use new for classes. ES2015 seems to agree with me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, changed it back to the original behavior (throw if they forget to use new).

@ariporad
Copy link
Contributor

@jamestalmage: Looks pretty good, expect for the few things I mentioned. Also, it looks like there aren't very many tests on the CachingPrecompiler itself (I don't know if there needs to be).

filename: filename,
sourceMaps: true,
ast: false,
inputSourceMap: sourceMap && sourceMap.toObject()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure to include the babelrc: false thing here.

@sindresorhus
Copy link
Member

Woo, nice improvement on cold start! 👍

Generally looks good to me. Could use a test though.

@vdemedes ?

@jamestalmage
Copy link
Contributor Author

could use a test though.

Yeah. How far do you want me to take it? For nyc, we run every test twice (once with caching on, once with caching off). That was easy to pull off with nyc due to how we build our test suite.

This is what I am thinking for tests:

  1. Test a fixture project with caching on. Once the test completes ensure there are X number of files in the cache directory.
  2. Delete the cache and test the same fixture from 1 again. Ensure the cache stays empty.
  3. Duplicate some of our source map tests with caching off? I might need @novemberborn's help here since I know next to zero about how a good source map test should look.

@sindresorhus
Copy link
Member

I don't think we should run all tests with and without. Just need some integration tests to ensure it's working correctly. What you've proposed sounds fine.

@jamestalmage jamestalmage force-pushed the transpile-in-main-thread branch 3 times, most recently from 785c6c8 to 8bc8387 Compare January 2, 2016 05:13
@@ -137,6 +144,11 @@ Api.prototype.run = function () {
return Promise.reject(new AvaError('Couldn\'t find any files to test'));
}

var cache = self.options.cache !== false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use isCacheEnabled, something more verbose & logical given the boolean variable value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about just cacheEnabled?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or that, yeah

@vadimdemedes
Copy link
Contributor

I really dig the perf improvement! I commented on a few things, but never mind. We can do a cleanup later, I'd rather have this PR merged ASAP.

Squashed commits:

transpile in the main thread

drop unused dependencies

incorporate PR feedback

add tests

add unit tests
@jamestalmage jamestalmage force-pushed the transpile-in-main-thread branch from 8bc8387 to 614eb12 Compare January 2, 2016 09:59
@jamestalmage
Copy link
Contributor Author

OK, Incorporated the last round of feedback from @vdemedes. Once CI passes again, I intend to merge.

jamestalmage added a commit that referenced this pull request Jan 2, 2016
@jamestalmage jamestalmage merged commit ce47844 into avajs:master Jan 2, 2016
@jamestalmage jamestalmage deleted the transpile-in-main-thread branch January 2, 2016 10:14
@jamestalmage
Copy link
Contributor Author

Landed.

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.

5 participants