Skip to content

Commit

Permalink
Cache micromatch in SearchSource globsToMatcher
Browse files Browse the repository at this point in the history
I was profiling some Jest runs at Airbnb and noticed that on my
MacBook Pro, we can spend over 2 seconds at Jest startup time in
SearchSource getTestPaths. I believe that this will grow as the size
of the codebase increases.

Looking at the call stacks, it appears to be calling micromatch
repeatedly, which calls picomatch, which builds a regex out of the
globs. It seems that the parsing and regex building also triggers the
garbage collector frequently.

Upon testing, I noticed that the globs don't actually change between
these calls, so we can safe a bunch of work by making a micromatch
matcher and reusing that function if the globs haven't changed. In my
basic testing, it is always called with the same set of globs, so I
went with a very simple last-used memoization instead of a more robust
caching solution.

In my profiling of this change locally, this brings down the time of
startRun from about 2000ms to about 200ms.
  • Loading branch information
lencioni committed Jun 5, 2020
1 parent 068ec04 commit e13b694
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

### Performance

- `[jest-core]` Cache micromatch in SearchSource globsToMatcher

## 26.0.1

### Fixes
Expand Down
12 changes: 10 additions & 2 deletions packages/jest-core/src/SearchSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,16 @@ export type TestSelectionConfig = {
watch?: boolean;
};

const globsToMatcher = (globs: Array<Config.Glob>) => (path: Config.Path) =>
micromatch([replacePathSepForGlob(path)], globs, {dot: true}).length > 0;
let prevGlobs: Array<Config.Glob>;
let prevGlobsMatchers: ((str: string) => boolean)[];
const globsToMatcher = (globs: Array<Config.Glob>) => {
if (prevGlobs !== globs) {
prevGlobs = globs;
prevGlobsMatchers = globs.map(glob => micromatch.matcher(glob, {dot: true}));
}

return (path: Config.Path) => prevGlobsMatchers.some(isMatch => isMatch(replacePathSepForGlob(path)));
}

const regexToMatcher = (testRegex: Config.ProjectConfig['testRegex']) => (
path: Config.Path,
Expand Down

0 comments on commit e13b694

Please sign in to comment.