【问题标题】:Does the .pipe() perform a memcpy in node.js?.pipe() 是否在 node.js 中执行 memcpy?
【发布时间】:2016-05-08 17:54:14
【问题描述】:

这是一个关于系统级优化的概念查询。通过阅读 NodeJS 文档,我的理解是管道可以方便地对流执行流控制。

背景:我有麦克风流进入,我想避免额外的复制操作以节省整个系统的 MIPS。我知道对于音频流,即使引擎盖下有一个 memcopy,也不会花费大量的 MIPS,但我还计划扩展一个扩展,以 30fps 和 UHD 分辨率流式传输摄像机帧。以 30fps 的速度制作多份 UHD 分辨率像素数据的效率非常低,因此需要一些建议。

示例代码:

var spawn = require('child_process').spawn
var PassThrough = require('stream').PassThrough;

var ps = null;
//var audioStream = new PassThrough;
//var infoStream = new PassThrough;

var start = function() {
    if(ps == null) {
        ps = spawn('rec', ['-b', 16, '--endian', 'little', '-c', 1, '-r', 16000, '-e', 'signed-integer', '-t', 'raw', '-']);
        //ps.stdout.pipe(audioStream);
        //ps.stderr.pipe(infoStream);
        exports.audioStream = ps.stdout;
        exports.infoStream = ps.stderr;
    }
};

var stop = function() {
    if(ps) {
        ps.kill();
        ps = null;
    }
};

//exports.audioStream = audioStream;
//exports.infoStream = infoStream;
exports.startCapture = start;
exports.stopCapture = stop;

以下是问题:

  1. 为了能够执行流控制,source.pipe(dest) 是在后台执行从源内存到目标内存的 memcpy,还是会将内存中的引用传递到目标?
  2. 注释的代码包含一个 PassThrough 类实例化 - 我目前假设 PassThrough 也会导致 memcopies,因此我在整个系统中保存了一个 memcpy 操作,因为我在上面的 cmets 中添加了?
  3. 如果我必须在进程和生成的子进程之间创建一个管道(使用 How to transfer/stream big data from/to child processes in node.js without using the blocking stdio? 中所示的 child_process.spawn()),我认为这肯定会导致 memcpy?反正有没有做参考而不是复制?
  4. 此行为是否因操作系统而异?我认为它应该与操作系统无关,但无论如何都要问这个问题。

提前感谢您的帮助。这将对我的架构有很大帮助。

【问题讨论】:

    标签: node.js pipe v8 libuv


    【解决方案1】:

    一些网址供参考:https://github.com/nodejs/node/
    https://github.com/nodejs/node/blob/master/src/stream_wrap.cc
    https://github.com/nodejs/node/blob/master/src/stream_base.cc
    https://github.com/libuv/libuv/blob/v1.x/src/unix/stream.c
    https://github.com/libuv/libuv/blob/v1.x/src/win/stream.c

    我尝试根据这些和其他一些文件编写一个复杂/巨大的解释,但是我得出的结论是最好给你一个总结,我的经验/阅读告诉我节点内部是如何工作的:

    管道只是连接流,使其看起来好像.write(…) 调用了.on("data", …),而两者之间没有任何膨胀。

    现在我们需要将 js 世界与 c++ / c 世界分开。
    在 js 中处理数据时,我们使用缓冲区。 https://github.com/nodejs/node/blob/master/src/node_buffer.cc
    它们只是代表分配的内存,顶部有一些糖果来操作它。

    如果您将进程的标准输出连接到某个.on("data", …) 侦听器,它会将传入的块复制到 Buffer 对象中,以便在 js 世界中进一步使用。
    在 js 世界中,您可以使用 .pause() 等方法(正如您在节点 steam api 文档中看到的那样),以防止进程占用内存,以防传入数据的流动速度超过其处理速度。

    连接一个进程的标准输出,例如通过管道传出的 tcp 端口将产生一个类似于 nginx 操作方式的连接。它将连接这些流,就好像它们通过将传入数据直接复制到传出流来直接相互通信一样。

    一旦你暂停一个流,节点将使用内部缓冲,以防它无法暂停传入的流。

    所以对于您的场景,您应该只进行测试。
    尝试通过节点中的传入流接收数据,暂停流,看看会发生什么。
    我不确定节点是否会使用内部缓冲,或者您尝试运行的进程是否会停止,直到它可以继续发送数据。
    我希望该过程会停止,直到您继续直播。

    对于传输巨大的图像,我建议将它们分块传输或将它们直接通过管道传输到传出端口。

    块方式允许您一次将数据发送到多个客户端,并将内存占用保持在相当低的水平。

    PS 你应该看看我刚刚发现的这个要点:https://gist.github.com/joyrexus/10026630
    它深入解释了如何与流交互

    【讨论】:

      猜你喜欢
      • 2011-07-11
      • 1970-01-01
      • 1970-01-01
      • 2014-04-21
      • 2013-04-21
      • 2013-12-03
      • 2019-05-05
      • 2022-01-19
      相关资源
      最近更新 更多