perf(ivy): ngcc - add a cache to the FileSystem (#30525)

When profiling ngcc it is notable that a large amount of time
is spent dealing with an exception that is thrown (and handled
internally by fs) when checking the existence of a file.

We check file existence a lot in both finding entry-points
and when TS is compiling code. This commit adds a simple
cached `FileSystem`, which wraps a real `FileSystem` delegate.
This will reduce the number of calls through to `fs.exists()` and
`fs.readFile()` on the delegate.

Initial benchmarks indicate that the cache is miss to hit ratio
for `exists()` is about 2:1, which means that we save about 1/3
of the calls to `fs.existsSync()`.

Note that this implements a "non-expiring" cache, so it is not suitable
for a long lived `FileSystem`, where files may be modified externally.
The cache will be updated if a file is changed or moved via
calls to `FileSystem` methods but it will not be aware of changes
to the files system from outside the `FileSystem` service.

For ngcc we must create a new `FileSystem` service
for each run of `mainNgcc` and ensure that all file operations
(including TS compilation) use the `FileSystem` service.
This ensures that it is very unlikely that a file will change
externally during `mainNgcc` processing.

PR Close #30525
This commit is contained in:
Pete Bacon Darwin
2019-05-15 13:38:19 +01:00
committed by Jason Aden
parent aaaeb924ac
commit 7f2330a968
5 changed files with 409 additions and 7 deletions

View File

@ -5,20 +5,25 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {CachedFileSystem, NodeJSFileSystem, setFileSystem} from '../src/ngtsc/file_system';
import {NodeJSFileSystem, setFileSystem} from '../src/ngtsc/file_system';
import {mainNgcc} from './src/main';
import {hasBeenProcessed as _hasBeenProcessed} from './src/packages/build_marker';
import {EntryPointJsonProperty, EntryPointPackageJson} from './src/packages/entry_point';
export {ConsoleLogger, LogLevel} from './src/logging/console_logger';
export {Logger} from './src/logging/logger';
export {NgccOptions, mainNgcc as process} from './src/main';
export {NgccOptions} from './src/main';
export {PathMappings} from './src/utils';
export function hasBeenProcessed(packageJson: object, format: string) {
// We are wrapping this function to hide the internal types.
// Recreate the file system on each call to reset the cache
setFileSystem(new CachedFileSystem(new NodeJSFileSystem()));
return _hasBeenProcessed(packageJson as EntryPointPackageJson, format as EntryPointJsonProperty);
}
// Configure the file-system for external users.
setFileSystem(new NodeJSFileSystem());
export function process(...args: Parameters<typeof mainNgcc>) {
// Recreate the file system on each call to reset the cache
setFileSystem(new CachedFileSystem(new NodeJSFileSystem()));
return mainNgcc(...args);
}

View File

@ -8,7 +8,7 @@
*/
import * as yargs from 'yargs';
import {resolve, setFileSystem, NodeJSFileSystem} from '../src/ngtsc/file_system';
import {resolve, setFileSystem, CachedFileSystem, NodeJSFileSystem} from '../src/ngtsc/file_system';
import {mainNgcc} from './src/main';
import {ConsoleLogger, LogLevel} from './src/logging/console_logger';
@ -57,7 +57,7 @@ if (require.main === module) {
process.exit(1);
}
setFileSystem(new NodeJSFileSystem());
setFileSystem(new CachedFileSystem(new NodeJSFileSystem()));
const baseSourcePath = resolve(options['s'] || './node_modules');
const propertiesToConsider: string[] = options['p'];