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

build: create V8 code cache after script is run #21567

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 38 additions & 11 deletions lib/internal/bootstrap/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,50 @@ const {
NativeModule, internalBinding
} = require('internal/bootstrap/loaders');

function getCodeCache(id) {
const cached = NativeModule.getCached(id);
if (cached && (cached.loaded || cached.loading)) {
return cached.script.createCachedData();
}

// The script has not been compiled and run
NativeModule.require(id);
return getCodeCache(id);
}

const depsModule = Object.keys(NativeModule._source).filter(
(key) => NativeModule.isDepsModule(key) || key.startsWith('internal/deps')
);

// Modules with source code compiled in js2c that
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I understand this comment. Why can't these scripts be cached?

Copy link
Member Author

@joyeecheung joyeecheung Jun 27, 2018

Choose a reason for hiding this comment

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

  • config is a JSON string, it's not going to be wrapped
  • sys is deprecated and is redirected from util (executing it would trigger a deprecation warning)
  • internal/test/binding triggers a warning as well (also, it's not supposed to be loaded by users)
  • internal/v8_prof_polyfill is not supposed to be required directly (see the comments there)
  • internal/v8_prof_processor is an actual script (instead of a module) that is supposed to write a log file

The files in v8/tools are not really Node.js modules. They are mostly concatenated and run by internal/v8_prof_processor since they expose stuff to the global space and rely on that.

Copy link
Member

Choose a reason for hiding this comment

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

I see. Thanks for the explanation.

// cannot be compiled with the code cache
const cannotUseCache = [
'config',
'sys', // deprecated
'internal/v8_prof_polyfill',
'internal/v8_prof_processor',

'internal/per_context',

'internal/test/binding',
// TODO(joyeecheung): update the C++ side so that
// the code cache is also used when compiling these
// two files.
'internal/bootstrap/loaders',
'internal/bootstrap/node'
].concat(depsModule);

module.exports = {
cachableBuiltins: Object.keys(NativeModule._source).filter(
(key) => !cannotUseCache.includes(key)
),
builtinSource: Object.assign({}, NativeModule._source),
getCodeCache,
codeCache: internalBinding('code_cache'),
compiledWithoutCache: NativeModule.compiledWithoutCache,
compiledWithCache: NativeModule.compiledWithCache,
nativeModuleWrap(script) {
return NativeModule.wrap(script);
},
// Modules with source code compiled in js2c that
// cannot be compiled with the code cache
cannotUseCache: [
'config',
// TODO(joyeecheung): update the C++ side so that
// the code cache is also used when compiling these
// two files.
'internal/bootstrap/loaders',
'internal/bootstrap/node',
'internal/per_context',
]
cannotUseCache
};
10 changes: 8 additions & 2 deletions lib/internal/bootstrap/loaders.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
this.exportKeys = undefined;
this.loaded = false;
this.loading = false;
this.script = null; // The ContextifyScript of the module
}

NativeModule._source = getBinding('natives');
Expand Down Expand Up @@ -165,11 +166,14 @@
return nativeModule.exports;
};

NativeModule.isDepsModule = function(id) {
return id.startsWith('node-inspect/') || id.startsWith('v8/');
};

NativeModule.requireForDeps = function(id) {
if (!NativeModule.exists(id) ||
// TODO(TimothyGu): remove when DEP0084 reaches end of life.
id.startsWith('node-inspect/') ||
id.startsWith('v8/')) {
NativeModule.isDepsModule(id)) {
id = `internal/deps/${id}`;
}
return NativeModule.require(id);
Expand Down Expand Up @@ -241,6 +245,8 @@
codeCache[this.id], false, undefined
);

this.script = script;

// One of these conditions may be false when any of the inputs
// of the `node_js2c` target in node.gyp is modified.
// FIXME(joyeecheung):
Expand Down
6 changes: 2 additions & 4 deletions test/code-cache/test-code-cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ const {
}
} = require('util');
const {
builtinSource,
cachableBuiltins,
codeCache,
cannotUseCache,
compiledWithCache,
compiledWithoutCache
} = require('internal/bootstrap/cache');
Expand All @@ -35,8 +34,7 @@ for (const key of loadedModules) {
`"${key}" should've been compiled with code cache`);
}

for (const key of Object.keys(builtinSource)) {
if (cannotUseCache.includes(key)) continue;
for (const key of cachableBuiltins) {
assert(isUint8Array(codeCache[key]) && codeCache[key].length > 0,
`Code cache for "${key}" should've been generated`);
}
25 changes: 7 additions & 18 deletions tools/generate_code_cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@
// of `configure`.

const {
nativeModuleWrap,
builtinSource,
cannotUseCache
getCodeCache,
cachableBuiltins
} = require('internal/bootstrap/cache');

const vm = require('vm');
const fs = require('fs');

const resultPath = process.argv[2];
Expand Down Expand Up @@ -72,25 +70,16 @@ const cacheInitializers = [];
let totalCacheSize = 0;


for (const key of Object.keys(builtinSource)) {
if (cannotUseCache.includes(key)) continue;
const code = nativeModuleWrap(builtinSource[key]);

// Note that this must corresponds to the code in
// NativeModule.prototype.compile
const script = new vm.Script(code, {
filename: `${key}.js`,
produceCachedData: true
});

if (!script.cachedData) {
for (const key of cachableBuiltins) {
const cachedData = getCodeCache(key);
if (!cachedData.length) {
console.error(`Failed to generate code cache for '${key}'`);
process.exit(1);
}

const length = script.cachedData.length;
const length = cachedData.length;
totalCacheSize += length;
const { definition, initializer } = getInitalizer(key, script.cachedData);
const { definition, initializer } = getInitalizer(key, cachedData);
cacheDefinitions.push(definition);
cacheInitializers.push(initializer);
console.log(`Generated cache for '${key}', size = ${formatSize(length)}` +
Expand Down