在 Node.js 和 C++ 之间使用 Buffer 共享数据

本文讲的是在 Node.js 和 C++ 之间使用 Buffer 共享数据,使用 Node.js 开发的一个好处是简直能够在 JavaScript 和 原生 C++ 代码之间无缝切换 - 这要得益于 V8 的扩展 API。从 JavaScript 进入 C++ 的能力有时由处理速度驱动,但更多的情况是我们已经有 C++ 代码,而我们想要直接用 Ja...
摘要由CSDN通过智能技术生成
本文讲的是在 Node.js 和 C++ 之间使用 Buffer 共享数据,

使用 Node.js 开发的一个好处是简直能够在 JavaScript 和 原生 C++ 代码之间无缝切换 - 这要得益于 V8 的扩展 API。从 JavaScript 进入 C++ 的能力有时由处理速度驱动,但更多的情况是我们已经有 C++ 代码,而我们想要直接用 JavaScript 调用。

我们可以用(至少)两轴对不同用例的扩展进行分类 - (1)C++ 代码的运行时间,(2)C++ 和 JavaScript 之间数据流量。

CPU vs. 数据象限

大多数文档讨论的 Node.js 的 C++ 扩展关注于左右象限的不同。如果你在左象限(短处理时间),你的扩展有可能是同步的 - 意思是当调用时 C++ 代码在 Node.js 的事件循环中直接运行。

"#nodejs 允许我们在#javascript 和原生 C++ 代码之间无缝切换" via @RisingStack

在这个场景中,扩展函数阻塞并等待返回值,意味着其他操作不能同时进行。在右侧象限中,几乎可以确定要用异步模式来设计附加组件。在一个异步扩展函数中,JavaScript 调用函数立即返回。调用代码向扩展函数传入一个回调,扩展函数工作于一个独立工作线程中。由于扩展函数没有阻塞,则避免了 Node.js 事件循环的死锁。

顶部和底部象限的不同时常容易被忽视,但是他们也同样重要。

V8 vs. C++ 内存和数据

如果你不了解如何写一个原生附件,那么你首先要掌握的是属于 V8 的数据(可以 通过 C++ 附件获取的)和普通 C++ 内存分配的区别。

当我们提到 “属于 V8 的”,指的是持有 JavaScript 数据的存储单元。

这些存储单元是可通过 V8 的 C++ API 访问的,但它们不是普通的 C++ 变量,因为他们只能够通过受限的方式访问。当你的扩展 可以 限制为只使用 V8 数据,它就更有可能同样会在普通 C++ 代码中创建自身的变量。这些变量可以是栈或堆变量,且完全独立于 V8。

在 JavaScript 中,基本类型(数字,字符串,布尔值等)是 不可变的,一个 C++ 扩展不能够改变与基本类型相连的存储单元。这些基本类型的 JavaScript 变量可以被重新分配到 C++ 创建的 新存储单元 中 - 但是这意味着改变数据将会导致  内存的分配。

在上层象限(少量数据传递),这没什么大不了。如果你正在设计一个无需频繁数据交换的附加组件,那么所有新内存分配的开销可能没有那么大。当扩展更靠近下层象限时,分配/拷贝的开销会开始令人震惊。

一方面,这会增大最高的内存使用量,另一方面,也会 损耗性能

在 JavaScript(V8 存储单元) 和 C++(返回)之间复制所有数据花费的时间通常会牺牲首先运行 C++ 赚来的性能红利!对于在左下象限(低处理,高数据利用场景)的扩展应用,数据拷贝的延迟会把你的扩展引用往右侧象限引导 - 迫使你考虑异步设计。

V8 内存与异步附件

在异步扩展中,我们在一个工作线程中执行大块的 C++ 处理代码。如果你对异步回调并不熟悉,看看这些教程(这里 和 这里)。

异步扩展的中心思想是 你不能在事件循环线程外访问 V8 (JavaScript)内存。这导致了新的问题。大量数据必须在工作线程启动前 从事件循环中 复制到 V8 内存之外,即扩展的原生地址空间中去。同样地,工作线程产生或修改的任何数据都必须通过执行事件循环(回调)中的代码拷贝回 V8 引擎。如果你致力于创建高吞吐量的 Node.js 应用,你应该避免花费过多的时间在事件循环的数据拷贝上。

为 C++ 工作线程创建输入输出拷贝

理想情况下,我们更倾向于这么做:

从 C++ 工作线程中直接访问 V8 数据

Node.js Buffer 来救命

这里有两个相关的问题。

  1. 当使用同步扩展时,除非我们不改变/产生数据,那么可能会需要花费大量时间在 V8 存储单元和老的简单 C++ 变量之间移动数据 - 十分费时。
  2. 当使用异步扩展时,理想情况下我们应该尽可能减少事件轮询的时间。这就是问题所在 - 由于 V8 的多线程限制,我们 必须 在事件轮询线程中进行数据拷贝。

Node.js 里有一个经常会被忽视的特性可以帮助我们进行扩展开发 - BufferNodes.js 官方文档 在此。

Buffer 类的实例与整型数组类似,但对应的是 V8 堆外大小固定,原始内存分配空间。

这不就是我们一直想要的吗 - Buffer 里的数据 并不存储在 V8 存储单元内,不受限于 V8 的多线程规则。这意味着可以通过异步扩展启动的 C++ 工作线程与 Buffer 进行交互。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值