av激情亚洲男人的天堂国语,日韩欧美精品一中文字幕,无码av一区二区三区无码,国产又色又爽又刺激的a片,国产又色又爽又刺激的a片

通過代碼緩存加速 Node.js 的啟動

前言:之前的文章介紹了通過快照的方式加速 Node.js 的啟動,除了快照,V8 還提供了另一種技術(shù)加速代碼的執(zhí)行,那就是代碼緩存。通過 V8 第一次執(zhí)行 JS 的時(shí)候,V8 需要即時(shí)進(jìn)行解析和編譯 JS代碼,這個(gè)是需要一定時(shí)間的,代碼緩存可以把這個(gè)過程的一些信息保存下來,下次執(zhí)行的時(shí)候,通過這個(gè)緩存的信息就可以加速 JS 代碼的執(zhí)行。本文介紹在 Node.js 里如何利用代碼緩存技術(shù)加速 Node.js 的啟動。

首先看一下 Node.js 的編譯配置。

'actions': [
{
'action_name': 'node_js2c',
'process_outputs_as_sources': 1,
'inputs': [
'tools/js2c.py',
'<@(library_files)',
'<@(deps_files)',
'config.gypi'
],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/node_javascript.cc',
],
'action': [
'<(python)',
'tools/js2c.py',
'--directory',
'lib',
'--target',
'<@(_outputs)',
'config.gypi',
'<@(deps_files)',
],
},
],

通過這個(gè)配置,在編譯 Node.js 的時(shí)候,會執(zhí)行 js2c.py,并且把輸入寫到 node_javascript.cc 文件。我們看一下生成的內(nèi)容。

里面定義了一個(gè)函數(shù),這個(gè)函數(shù)里面往 source_ 字段里不斷追加一系列的內(nèi)容,其中 key 是 Node.js 中的原生 JS 模塊信息,值是模塊的內(nèi)容,我們隨便看一個(gè)模塊 assert/strict。

const data = [39,117,115,101, 32,115,116,114,105, 99,116, 39, 59, 10, 10,109,111,100,117,108,101, 46,101,120,112,111,114,116,115, 32,61, 32,114,101,113,117,105,114,101, 40, 39, 97,115,115,101,114,116, 39, 41, 46,115,116,114,105, 99,116, 59, 10];
console.log(Buffer.from(data).toString('utf-8'))

輸出如下。

'use strict';
module.exports = require('assert').strict;

通過 js2c.py 腳本,Node.js 把原生 JS 模塊的內(nèi)容寫到了文件中,并且編譯進(jìn) Node.js 的可執(zhí)行文件里,這樣在 Node.js 啟動時(shí)就不需要從硬盤里讀取對應(yīng)的文件,否則無論是啟動還是運(yùn)行時(shí)動態(tài)加載原生 JS 模塊,都需要更多的耗時(shí),因?yàn)閮?nèi)存的速度遠(yuǎn)快于硬盤。這是 Node.js 做的第一個(gè)優(yōu)化,接下來看代碼緩存,因?yàn)榇a緩存是在這個(gè)基礎(chǔ)上實(shí)現(xiàn)的。首先看一下編譯配置。

['node_use_node_code_cache=="true"', {
'dependencies': [
'mkcodecache',
],
'actions': [
{
'action_name': 'run_mkcodecache',
'process_outputs_as_sources': 1,
'inputs': [
'<(mkcodecache_exec)',
],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/node_code_cache.cc',
],
'action': [
'<@(_inputs)',
'<@(_outputs)',
],
},
],}, {
'sources': [
'src/node_code_cache_stub.cc'
],
}],

如果編譯 Node.js 時(shí) node_use_node_code_cache 為 true 則生成代碼緩存。如果我們不需要可以關(guān)掉,具體執(zhí)行 ./configure --without-node-code-cache。如果我們關(guān)閉代碼緩存, Node.js 關(guān)于這部分的實(shí)現(xiàn)是空,具體在 node_code_cache_stub.cc。

const bool has_code_cache = false;
void NativeModuleEnv::InitializeCodeCache() {}

也就是什么都不做。如果我們開啟了代碼緩存,就會執(zhí)行 mkcodecache.cc 生成代碼緩存。

int main(int argc, char* argv[]) {
argv = uv_setup_args(argc, argv);
std::ofstream out;
out.open(argv[1], std::ios::out | std::ios::binary);
node::per_process::enabled_debug_list.Parse(nullptr);
std::unique_ptr platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
Isolate::CreateParams create_params;
create_params.array_buffer_allocator_shared.reset(
ArrayBuffer::Allocator::NewDefaultAllocator());
Isolate* isolate = Isolate::New(create_params);
{
Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
std::string cache = CodeCacheBuilder::Generate(context);
out << cache;
out.close();
}
isolate->Dispose();
v8::V8::ShutdownPlatform();
return 0;
}

首先打開文件,然后是 V8 的常用初始化邏輯,最后通過 Generate 生成代碼緩存。

std::string CodeCacheBuilder::Generate(Local context) {
NativeModuleLoader* loader = NativeModuleLoader::GetInstance();
std::vector ids = loader->GetModuleIds();
std::map data;
for (const auto& id : ids) {
if (loader->CanBeRequired(id.c_str())) {
NativeModuleLoader::Result result;
USE(loader->CompileAsModule(context, id.c_str(), &result));
ScriptCompiler::CachedData* cached_data = loader->GetCodeCache(id.c_str());
data.emplace(id, cached_data);
}
}
return GenerateCodeCache(data);
}

首先新建一個(gè) NativeModuleLoader。

NativeModuleLoader::NativeModuleLoader() : config_(GetConfig()) {
LoadJavaScriptSource();
}

NativeModuleLoader 初始化時(shí)會執(zhí)行 LoadJavaScriptSource,這個(gè)函數(shù)就是通過 python 腳本生成的 node_javascript.cc 文件里的函數(shù),初始化完成后 NativeModuleLoader 對象的 source_ 字段就保存了原生 JS 模塊的代碼。接著遍歷這些原生 JS 模塊,通過 CompileAsModule 進(jìn)行編譯。

MaybeLocal NativeModuleLoader::CompileAsModule(
Local context,
const char* id,
NativeModuleLoader::Result* result) {
Isolate* isolate = context->GetIsolate();
std::vector> parameters = {
FIXED_ONE_BYTE_STRING(isolate, "exports"),
FIXED_ONE_BYTE_STRING(isolate, "require"),
FIXED_ONE_BYTE_STRING(isolate, "module"),
FIXED_ONE_BYTE_STRING(isolate, "process"),
FIXED_ONE_BYTE_STRING(isolate, "internalBinding"),
FIXED_ONE_BYTE_STRING(isolate, "primordials")};
return LookupAndCompile(context, id, ¶meters, result);
}

接著看 LookupAndCompile

MaybeLocal NativeModuleLoader::LookupAndCompile(
Local context,
const char* id,
std::vector>* parameters,
NativeModuleLoader::Result* result) {
Isolate* isolate = context->GetIsolate();
EscapableHandleScope scope(isolate);
Local source;
// 根據(jù) key 從 source_ 字段找到模塊內(nèi)容
if (!LoadBuiltinModuleSource(isolate, id).ToLocal(&source)) {
return {};
}
std::string filename_s = std::string("node:") + id;
Local filename =
OneByteString(isolate, filename_s.c_str(), filename_s.size());
ScriptOrigin origin(isolate, filename, 0, 0, true);
ScriptCompiler::CachedData* cached_data = nullptr;
{
Mutex::ScopedLock lock(code_cache_mutex_);
// 判斷是否有代碼緩存
auto cache_it = code_cache_.find(id);
if (cache_it != code_cache_.end()) {
cached_data = cache_it->second.release();
code_cache_.erase(cache_it);
}
}
const bool has_cache = cached_data != nullptr;
ScriptCompiler::CompileOptions options =
has_cache ? ScriptCompiler::kConsumeCodeCache
: ScriptCompiler::kEagerCompile;
// 如果有代碼緩存則傳入
ScriptCompiler::Source script_source(source, origin, cached_data);
// 進(jìn)行編譯
MaybeLocal maybe_fun =
ScriptCompiler::CompileFunctionInContext(context,
&script_source,
parameters->size(),
parameters->data(),
0,
nullptr,
options);
Local fun;
if (!maybe_fun.ToLocal(&fun)) {
return MaybeLocal();
}
*result = (has_cache && !script_source.GetCachedData()->rejected)
? Result::kWithCache
: Result::kWithoutCache;
// 生成代碼緩存保存下來,最后寫入文件,下次使用
std::unique_ptr new_cached_data(
ScriptCompiler::CreateCodeCacheForFunction(fun));
{
Mutex::ScopedLock lock(code_cache_mutex_);
code_cache_.emplace(id, std::move(new_cached_data));
}
return scope.Escape(fun);
}

第一次執(zhí)行的時(shí)候,也就是編譯 Node.js 時(shí),LookupAndCompile 會生成代碼緩存寫到文件 node_code_cache.cc 中,并編譯進(jìn)可執(zhí)行文件,內(nèi)容大致如下。

除了這個(gè)函數(shù)還有一系列的代碼緩存數(shù)據(jù),這里就不貼出來了。在 Node.js 第一次執(zhí)行的初始化階段,就會執(zhí)行上面的函數(shù),在 code_cache 字段里保存了每個(gè)模塊和對應(yīng)的代碼緩存。初始化完畢后,后面加載原生 JS 模塊時(shí),Node.js 再次執(zhí)行 LookupAndCompile,就個(gè)時(shí)候就有代碼緩存了。當(dāng)開啟代碼緩存時(shí),我的電腦上 Node.js 啟動時(shí)間大概為 40 毫秒,當(dāng)去掉代碼緩存的邏輯重新編譯后,Node.js 的啟動時(shí)間大概是 60 毫秒,速度有了很大的提升。

總結(jié):Node.js 在編譯時(shí)首先把原生 JS 模塊的代碼寫入到文件并,接著執(zhí)行 mkcodecache.cc 把原生 JS 模塊進(jìn)行編譯和獲取對應(yīng)的代碼緩存,然后寫到文件中,同時(shí)編譯進(jìn) Node.js 的可執(zhí)行文件中,在 Node.js 初始化時(shí)會把他們收集起來,這樣后續(xù)加載原生 JS 模塊時(shí)就可以使用這些代碼緩存加速代碼的執(zhí)行。


文章標(biāo)題:通過代碼緩存加速 Node.js 的啟動
文章分享:http://uogjgqi.cn/article/djgjoic.html
掃二維碼與項(xiàng)目經(jīng)理溝通

我們在微信上24小時(shí)期待你的聲音

解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流