阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

新技能:通过代码缓存加速 Node.js 的启动

266次阅读
没有评论

共计 4750 个字符,预计需要花费 12 分钟才能阅读完成。

导读 本文介绍在 Node.js 里如何利用代码缓存技术加速 Node.js 的启动。

新技能:通过代码缓存加速 Node.js 的启动
前言:之前的文章介绍了通过快照的方式加速 Node.js 的启动,除了快照,V8 还提供了另一种技术加速代码的执行,那就是代码缓存。通过 V8 第一次执行 JS 的时候,V8 需要即时进行解析和编译 JS 代码,这个是需要一定时间的,代码缓存可以把这个过程的一些信息保存下来,下次执行的时候,通过这个缓存的信息就可以加速 JS 代码的执行。本文介绍在 Node.js 里如何利用代码缓存技术加速 Node.js 的启动。

首先看一下 Node.js 的编译配置。

'actions': [
  {
    'action_name': 'node_js2c',
    'process_outputs_as_sources': 1,
    'inputs': [
      'tools/js2c.py',
      '

通过这个配置,在编译 Node.js 的时候,会执行 js2c.py,并且把输入写到 node_javascript.cc 文件。我们看一下生成的内容。
新技能:通过代码缓存加速 Node.js 的启动
里面定义了一个函数,这个函数里面往 source_ 字段里不断追加一系列的内容,其中 key 是 Node.js 中的原生 JS 模块信息,值是模块的内容,我们随便看一个模块 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 模块的内容写到了文件中,并且编译进 Node.js 的可执行文件里,这样在 Node.js 启动时就不需要从硬盘里读取对应的文件,否则无论是启动还是运行时动态加载原生 JS 模块,都需要更多的耗时,因为内存的速度远快于硬盘。这是 Node.js 做的第一个优化,接下来看代码缓存,因为代码缓存是在这个基础上实现的。首先看一下编译配置。

['node_use_node_code_cache=="true"', {'dependencies': ['mkcodecache',],
  'actions': [
    {
      'action_name': 'run_mkcodecache',
      'process_outputs_as_sources': 1,
      'inputs': [
        '

如果编译 Node.js 时 node_use_node_code_cache 为 true 则生成代码缓存。如果我们不需要可以关掉,具体执行 ./configure --without-node-code-cache。如果我们关闭代码缓存,Node.js 关于这部分的实现是空,具体在 node_code_cache_stub.cc。

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

也就是什么都不做。如果我们开启了代码缓存,就会执行 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> 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> context = v8::Context::New(isolate);
    v8::Context::Scope context_scope(context);
    std::string cache = CodeCacheBuilder::Generate(context);
    out Dispose();
  v8::V8::ShutdownPlatform();
  return 0;
}

首先打开文件,然后是 V8 的常用初始化逻辑,最后通过 Generate 生成代码缓存。

std::string CodeCacheBuilder::Generate(Local context) {NativeModuleLoader* loader = NativeModuleLoader::GetInstance();
  std::vector<:string> ids = loader->GetModuleIds();
  std::map<:string scriptcompiler::cacheddata> 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);
}

首先新建一个 NativeModuleLoader。

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

NativeModuleLoader 初始化时会执行 LoadJavaScriptSource,这个函数就是通过 python 脚本生成的 node_javascript.cc 文件里的函数,初始化完成后 NativeModuleLoader 对象的 source_ 字段就保存了原生 JS 模块的代码。接着遍历这些原生 JS 模块,通过 CompileAsModule 进行编译。

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, &parameters, 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;
  // 根据 key 从 source_ 字段找到模块内容
  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);
  // 进行编译
  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        
	

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-07-25发表,共计4750字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中

星哥玩云

星哥玩云
星哥玩云
分享互联网知识
用户数
4
文章数
19348
评论数
4
阅读量
7807201
文章搜索
热门文章
开发者必备神器:阿里云 Qoder CLI 全面解析与上手指南

开发者必备神器:阿里云 Qoder CLI 全面解析与上手指南

开发者必备神器:阿里云 Qoder CLI 全面解析与上手指南 大家好,我是星哥。之前介绍了腾讯云的 Code...
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
云服务器部署服务器面板1Panel:小白轻松构建Web服务与面板加固指南

云服务器部署服务器面板1Panel:小白轻松构建Web服务与面板加固指南

云服务器部署服务器面板 1Panel:小白轻松构建 Web 服务与面板加固指南 哈喽,我是星哥,经常有人问我不...
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛 NAS-3:安装飞牛 NAS 后的很有必要的操作 前言 如果你已经有了飞牛 NAS 系统,之前...
阿里云CDN
阿里云CDN-提高用户访问的响应速度和成功率
随机文章
仅2MB大小!开源硬件监控工具:Win11 无缝适配,CPU、GPU、网速全维度掌控

仅2MB大小!开源硬件监控工具:Win11 无缝适配,CPU、GPU、网速全维度掌控

还在忍受动辄数百兆的“全家桶”监控软件?后台偷占资源、界面杂乱冗余,想查个 CPU 温度都要层层点选? 今天给...
免费领取huggingface的2核16G云服务器,超简单教程

免费领取huggingface的2核16G云服务器,超简单教程

免费领取 huggingface 的 2 核 16G 云服务器,超简单教程 前言 HuggingFace.co...
150元打造低成本NAS小钢炮,捡一块3865U工控板

150元打造低成本NAS小钢炮,捡一块3865U工控板

150 元打造低成本 NAS 小钢炮,捡一块 3865U 工控板 一块二手的熊猫 B3 工控板 3865U,搭...
安装Black群晖DSM7.2系统安装教程(在Vmware虚拟机中、实体机均可)!

安装Black群晖DSM7.2系统安装教程(在Vmware虚拟机中、实体机均可)!

安装 Black 群晖 DSM7.2 系统安装教程(在 Vmware 虚拟机中、实体机均可)! 前言 大家好,...
我用AI做了一个1978年至2019年中国大陆企业注册的查询网站

我用AI做了一个1978年至2019年中国大陆企业注册的查询网站

我用 AI 做了一个 1978 年至 2019 年中国大陆企业注册的查询网站 最近星哥在 GitHub 上偶然...

免费图片视频管理工具让灵感库告别混乱

一言一句话
-「
手气不错
如何免费使用强大的Nano Banana Pro?附赠邪修的用法

如何免费使用强大的Nano Banana Pro?附赠邪修的用法

如何免费使用强大的 Nano Banana Pro?附赠邪修的用法 前言 大家好,我是星哥,今天来介绍谷歌的 ...
国产开源公众号AI知识库 Agent:突破未认证号限制,一键搞定自动回复,重构运营效率

国产开源公众号AI知识库 Agent:突破未认证号限制,一键搞定自动回复,重构运营效率

国产开源公众号 AI 知识库 Agent:突破未认证号限制,一键搞定自动回复,重构运营效率 大家好,我是星哥,...
星哥带你玩飞牛NAS硬件 01:捡垃圾的最爱双盘,暴风二期矿渣为何成不老神话?

星哥带你玩飞牛NAS硬件 01:捡垃圾的最爱双盘,暴风二期矿渣为何成不老神话?

星哥带你玩飞牛 NAS 硬件 01:捡垃圾的最爱双盘,暴风二期矿渣为何成不老神话? 前言 在选择 NAS 用预...
240 元左右!五盘位 NAS主机,7 代U硬解4K稳如狗,拓展性碾压同价位

240 元左右!五盘位 NAS主机,7 代U硬解4K稳如狗,拓展性碾压同价位

  240 元左右!五盘位 NAS 主机,7 代 U 硬解 4K 稳如狗,拓展性碾压同价位 在 NA...
星哥带你玩飞牛NAS-5:飞牛NAS中的Docker功能介绍

星哥带你玩飞牛NAS-5:飞牛NAS中的Docker功能介绍

星哥带你玩飞牛 NAS-5:飞牛 NAS 中的 Docker 功能介绍 大家好,我是星哥,今天给大家带来如何在...